summaryrefslogtreecommitdiff
path: root/src/compiler/glsl/gl_nir_linker.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler/glsl/gl_nir_linker.c')
-rw-r--r--src/compiler/glsl/gl_nir_linker.c2442
1 files changed, 2393 insertions, 49 deletions
diff --git a/src/compiler/glsl/gl_nir_linker.c b/src/compiler/glsl/gl_nir_linker.c
index 9a93285c6d3..e12296c67c9 100644
--- a/src/compiler/glsl/gl_nir_linker.c
+++ b/src/compiler/glsl/gl_nir_linker.c
@@ -22,20 +22,397 @@
*/
#include "nir.h"
+#include "nir_builder.h"
+#include "nir_xfb_info.h"
#include "gl_nir.h"
#include "gl_nir_linker.h"
+#include "gl_nir_link_varyings.h"
#include "linker_util.h"
-#include "main/mtypes.h"
+#include "string_to_uint_map.h"
+#include "main/shader_types.h"
+#include "main/consts_exts.h"
+#include "main/context.h"
#include "main/shaderobj.h"
#include "ir_uniform.h" /* for gl_uniform_storage */
+#include "util/glheader.h"
+#include "util/perf/cpu_trace.h"
/**
* This file included general link methods, using NIR, instead of IR as
* the counter-part glsl/linker.cpp
*/
+void
+gl_nir_opts(nir_shader *nir)
+{
+ bool progress;
+
+ MESA_TRACE_FUNC();
+
+ do {
+ progress = false;
+
+ NIR_PASS(_, nir, nir_lower_vars_to_ssa);
+
+ /* Linking deals with unused inputs/outputs, but here we can remove
+ * things local to the shader in the hopes that we can cleanup other
+ * things. This pass will also remove variables with only stores, so we
+ * might be able to make progress after it.
+ */
+ NIR_PASS(progress, nir, nir_remove_dead_variables,
+ nir_var_function_temp | nir_var_shader_temp |
+ nir_var_mem_shared,
+ NULL);
+
+ NIR_PASS(progress, nir, nir_opt_find_array_copies);
+ NIR_PASS(progress, nir, nir_opt_copy_prop_vars);
+ NIR_PASS(progress, nir, nir_opt_dead_write_vars);
+
+ if (nir->options->lower_to_scalar) {
+ NIR_PASS(_, nir, nir_lower_alu_to_scalar,
+ nir->options->lower_to_scalar_filter, NULL);
+ NIR_PASS(_, nir, nir_lower_phis_to_scalar, false);
+ }
+
+ NIR_PASS(_, nir, nir_lower_alu);
+ NIR_PASS(_, nir, nir_lower_pack);
+ NIR_PASS(progress, nir, nir_copy_prop);
+ NIR_PASS(progress, nir, nir_opt_remove_phis);
+ NIR_PASS(progress, nir, nir_opt_dce);
+ if (nir_opt_loop(nir)) {
+ progress = true;
+ NIR_PASS(progress, nir, nir_copy_prop);
+ NIR_PASS(progress, nir, nir_opt_dce);
+ }
+ NIR_PASS(progress, nir, nir_opt_if, 0);
+ NIR_PASS(progress, nir, nir_opt_dead_cf);
+ NIR_PASS(progress, nir, nir_opt_cse);
+ NIR_PASS(progress, nir, nir_opt_peephole_select, 8, true, true);
+
+ NIR_PASS(progress, nir, nir_opt_phi_precision);
+ NIR_PASS(progress, nir, nir_opt_algebraic);
+ NIR_PASS(progress, nir, nir_opt_constant_folding);
+
+ if (!nir->info.flrp_lowered) {
+ unsigned lower_flrp =
+ (nir->options->lower_flrp16 ? 16 : 0) |
+ (nir->options->lower_flrp32 ? 32 : 0) |
+ (nir->options->lower_flrp64 ? 64 : 0);
+
+ if (lower_flrp) {
+ bool lower_flrp_progress = false;
+
+ NIR_PASS(lower_flrp_progress, nir, nir_lower_flrp,
+ lower_flrp,
+ false /* always_precise */);
+ if (lower_flrp_progress) {
+ NIR_PASS(progress, nir,
+ nir_opt_constant_folding);
+ progress = true;
+ }
+ }
+
+ /* Nothing should rematerialize any flrps, so we only need to do this
+ * lowering once.
+ */
+ nir->info.flrp_lowered = true;
+ }
+
+ NIR_PASS(progress, nir, nir_opt_undef);
+ NIR_PASS(progress, nir, nir_opt_conditional_discard);
+ if (nir->options->max_unroll_iterations ||
+ (nir->options->max_unroll_iterations_fp64 &&
+ (nir->options->lower_doubles_options & nir_lower_fp64_full_software))) {
+ NIR_PASS(progress, nir, nir_opt_loop_unroll);
+ }
+ } while (progress);
+
+ NIR_PASS(_, nir, nir_lower_var_copies);
+}
+
+void
+gl_nir_inline_functions(nir_shader *shader)
+{
+ /* We have to lower away local constant initializers right before we
+ * inline functions. That way they get properly initialized at the top
+ * of the function and not at the top of its caller.
+ */
+ NIR_PASS(_, shader, nir_lower_variable_initializers, nir_var_all);
+ NIR_PASS(_, shader, nir_lower_returns);
+ NIR_PASS(_, shader, nir_inline_functions);
+ NIR_PASS(_, shader, nir_opt_deref);
+
+ nir_validate_shader(shader, "after function inlining and return lowering");
+
+ /* We set func->is_entrypoint after nir_function_create if the function
+ * is named "main", so we can use nir_remove_non_entrypoints() for this.
+ * Now that we have inlined everything remove all of the functions except
+ * func->is_entrypoint.
+ */
+ nir_remove_non_entrypoints(shader);
+}
+
+struct emit_vertex_state {
+ int max_stream_allowed;
+ int invalid_stream_id;
+ bool invalid_stream_id_from_emit_vertex;
+ bool end_primitive_found;
+ unsigned used_streams;
+};
+
+/**
+ * Determine the highest stream id to which a (geometry) shader emits
+ * vertices. Also check whether End{Stream}Primitive is ever called.
+ */
+static void
+find_emit_vertex(struct emit_vertex_state *state, nir_shader *shader) {
+ nir_function_impl *impl = nir_shader_get_entrypoint(shader);
+
+ nir_foreach_block_safe(block, impl) {
+ nir_foreach_instr_safe(instr, block) {
+ if (instr->type == nir_instr_type_intrinsic) {
+ nir_intrinsic_instr *intr = nir_instr_as_intrinsic(instr);
+
+ if (intr->intrinsic == nir_intrinsic_emit_vertex ||
+ intr->intrinsic == nir_intrinsic_end_primitive) {
+ int stream_id = nir_intrinsic_stream_id(intr);
+ bool from_emit_vertex =
+ intr->intrinsic == nir_intrinsic_emit_vertex;
+ state->end_primitive_found |=
+ intr->intrinsic == nir_intrinsic_end_primitive;
+
+ if (stream_id < 0) {
+ state->invalid_stream_id = stream_id;
+ state->invalid_stream_id_from_emit_vertex = from_emit_vertex;
+ return;
+ }
+
+ if (stream_id > state->max_stream_allowed) {
+ state->invalid_stream_id = stream_id;
+ state->invalid_stream_id_from_emit_vertex = from_emit_vertex;
+ return;
+ }
+
+ state->used_streams |= 1 << stream_id;
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Check if geometry shaders emit to non-zero streams and do corresponding
+ * validations.
+ */
+static void
+validate_geometry_shader_emissions(const struct gl_constants *consts,
+ struct gl_shader_program *prog)
+{
+ struct gl_linked_shader *sh = prog->_LinkedShaders[MESA_SHADER_GEOMETRY];
+
+ if (sh != NULL) {
+ struct emit_vertex_state state;
+ state.max_stream_allowed = consts->MaxVertexStreams - 1;
+ state.invalid_stream_id = 0;
+ state.invalid_stream_id_from_emit_vertex = false;
+ state.end_primitive_found = false;
+ state.used_streams = 0;
+
+ find_emit_vertex(&state, sh->Program->nir);
+
+ if (state.invalid_stream_id != 0) {
+ linker_error(prog, "Invalid call %s(%d). Accepted values for the "
+ "stream parameter are in the range [0, %d].\n",
+ state.invalid_stream_id_from_emit_vertex ?
+ "EmitStreamVertex" : "EndStreamPrimitive",
+ state.invalid_stream_id, state.max_stream_allowed);
+ }
+ sh->Program->nir->info.gs.active_stream_mask = state.used_streams;
+ sh->Program->nir->info.gs.uses_end_primitive = state.end_primitive_found;
+
+ /* From the ARB_gpu_shader5 spec:
+ *
+ * "Multiple vertex streams are supported only if the output primitive
+ * type is declared to be "points". A program will fail to link if it
+ * contains a geometry shader calling EmitStreamVertex() or
+ * EndStreamPrimitive() if its output primitive type is not "points".
+ *
+ * However, in the same spec:
+ *
+ * "The function EmitVertex() is equivalent to calling EmitStreamVertex()
+ * with <stream> set to zero."
+ *
+ * And:
+ *
+ * "The function EndPrimitive() is equivalent to calling
+ * EndStreamPrimitive() with <stream> set to zero."
+ *
+ * Since we can call EmitVertex() and EndPrimitive() when we output
+ * primitives other than points, calling EmitStreamVertex(0) or
+ * EmitEndPrimitive(0) should not produce errors. This it also what Nvidia
+ * does. We can use info.gs.active_stream_mask to check whether only the
+ * first (zero) stream is active.
+ * stream.
+ */
+ if (sh->Program->nir->info.gs.active_stream_mask & ~(1 << 0) &&
+ sh->Program->info.gs.output_primitive != MESA_PRIM_POINTS) {
+ linker_error(prog, "EmitStreamVertex(n) and EndStreamPrimitive(n) "
+ "with n>0 requires point output\n");
+ }
+ }
+}
+
+/**
+ * Generate a string describing the mode of a variable
+ */
+const char *
+gl_nir_mode_string(const nir_variable *var)
+{
+ switch (var->data.mode) {
+ case nir_var_shader_temp:
+ return (var->data.read_only) ? "global constant" : "global variable";
+
+ case nir_var_uniform:
+ case nir_var_image:
+ case nir_var_mem_ubo:
+ return "uniform";
+
+ case nir_var_mem_ssbo:
+ return "buffer";
+
+ case nir_var_shader_in:
+ return "shader input";
+
+ case nir_var_shader_out:
+ return "shader output";
+
+ case nir_var_system_value:
+ return "shader input";
+
+ case nir_var_function_temp:
+ return "local variable";
+
+ case nir_var_mem_shared:
+ return "shader shared";
+
+ case nir_num_variable_modes:
+ break;
+ }
+
+ assert(!"Should not get here.");
+ return "invalid variable";
+}
+
+static void
+remove_dead_functions(nir_shader *shader)
+{
+ struct set *fn_set =
+ _mesa_set_create(NULL, _mesa_hash_pointer, _mesa_key_pointer_equal);
+
+ /* Find all function prototypes adding them to a list then removing them
+ * if they are ever called.
+ */
+ nir_foreach_function_impl(impl, shader) {
+ _mesa_set_add(fn_set, impl->function);
+ }
+
+ nir_foreach_function_impl(impl, shader) {
+ nir_foreach_block(block, impl) {
+ nir_foreach_instr(instr, block) {
+ if (instr->type == nir_instr_type_call) {
+ nir_call_instr *call = nir_instr_as_call(instr);
+ _mesa_set_remove_key(fn_set, call->callee);
+ }
+ }
+ }
+ }
+
+ /* Any functions remaining in the list must be unused so remove them. */
+ set_foreach(fn_set, entry) {
+ nir_function *func = (nir_function *) entry->key;
+ if (!func->is_entrypoint)
+ exec_node_remove(&func->node);
+ }
+
+ _mesa_set_destroy(fn_set, NULL);
+}
+
+bool
+gl_nir_can_add_pointsize_to_program(const struct gl_constants *consts,
+ struct gl_program *prog)
+{
+ nir_shader *nir = prog->nir;
+ if (!nir)
+ return true; /* fixedfunction */
+
+ assert(nir->info.stage == MESA_SHADER_VERTEX ||
+ nir->info.stage == MESA_SHADER_TESS_EVAL ||
+ nir->info.stage == MESA_SHADER_GEOMETRY);
+ if (nir->info.outputs_written & VARYING_BIT_PSIZ)
+ return false;
+
+ unsigned max_components = nir->info.stage == MESA_SHADER_GEOMETRY ?
+ consts->MaxGeometryTotalOutputComponents :
+ consts->Program[nir->info.stage].MaxOutputComponents;
+ unsigned num_components = 0;
+ unsigned needed_components = nir->info.stage == MESA_SHADER_GEOMETRY ? nir->info.gs.vertices_out : 1;
+ nir_foreach_shader_out_variable(var, nir) {
+ num_components += glsl_count_dword_slots(var->type, false);
+ }
+
+ /* Ensure that there is enough attribute space to emit at least one primitive */
+ if (num_components && nir->info.stage == MESA_SHADER_GEOMETRY) {
+ if (num_components + needed_components > consts->Program[nir->info.stage].MaxOutputComponents)
+ return false;
+ num_components *= nir->info.gs.vertices_out;
+ }
+
+ return num_components + needed_components <= max_components;
+}
+
+static void
+gl_nir_link_opts(nir_shader *producer, nir_shader *consumer)
+{
+ MESA_TRACE_FUNC();
+
+ if (producer->options->lower_to_scalar) {
+ NIR_PASS(_, producer, nir_lower_io_to_scalar_early, nir_var_shader_out);
+ NIR_PASS(_, consumer, nir_lower_io_to_scalar_early, nir_var_shader_in);
+ }
+
+ nir_lower_io_arrays_to_elements(producer, consumer);
+
+ gl_nir_opts(producer);
+ gl_nir_opts(consumer);
+
+ if (nir_link_opt_varyings(producer, consumer))
+ gl_nir_opts(consumer);
+
+ NIR_PASS(_, producer, nir_remove_dead_variables, nir_var_shader_out, NULL);
+ NIR_PASS(_, consumer, nir_remove_dead_variables, nir_var_shader_in, NULL);
+
+ if (nir_remove_unused_varyings(producer, consumer)) {
+ NIR_PASS(_, producer, nir_lower_global_vars_to_local);
+ NIR_PASS(_, consumer, nir_lower_global_vars_to_local);
+
+ gl_nir_opts(producer);
+ gl_nir_opts(consumer);
+
+ /* Optimizations can cause varyings to become unused.
+ * nir_compact_varyings() depends on all dead varyings being removed so
+ * we need to call nir_remove_dead_variables() again here.
+ */
+ NIR_PASS(_, producer, nir_remove_dead_variables, nir_var_shader_out,
+ NULL);
+ NIR_PASS(_, consumer, nir_remove_dead_variables, nir_var_shader_in,
+ NULL);
+ }
+
+ nir_link_varying_precision(producer, consumer);
+}
+
static bool
-can_remove_uniform(nir_variable *var, UNUSED void *data)
+can_remove_var(nir_variable *var, UNUSED void *data)
{
/* Section 2.11.6 (Uniform Variables) of the OpenGL ES 3.0.3 spec
* says:
@@ -59,20 +436,77 @@ can_remove_uniform(nir_variable *var, UNUSED void *data)
GLSL_TYPE_SUBROUTINE)
return false;
- /* Uniform initializers could get used by another stage */
- if (var->constant_initializer)
+ /* Uniform initializers could get used by another stage. However if its a
+ * hidden uniform then it should be safe to remove as this was a constant
+ * variable that has been lowered to a uniform.
+ */
+ if (var->constant_initializer && var->data.how_declared != nir_var_hidden)
return false;
return true;
}
+static void
+set_always_active_io(nir_shader *shader, nir_variable_mode io_mode)
+{
+ assert(io_mode == nir_var_shader_in || io_mode == nir_var_shader_out);
+
+ nir_foreach_variable_with_modes(var, shader, io_mode) {
+ /* Don't set always active on builtins that haven't been redeclared */
+ if (var->data.how_declared == nir_var_declared_implicitly)
+ continue;
+
+ var->data.always_active_io = true;
+ }
+}
+
/**
- * Built-in / reserved GL variables names start with "gl_"
+ * When separate shader programs are enabled, only input/outputs between
+ * the stages of a multi-stage separate program can be safely removed
+ * from the shader interface. Other inputs/outputs must remain active.
*/
-static inline bool
-is_gl_identifier(const char *s)
+static void
+disable_varying_optimizations_for_sso(struct gl_shader_program *prog)
{
- return s && s[0] == 'g' && s[1] == 'l' && s[2] == '_';
+ unsigned first, last;
+ assert(prog->SeparateShader);
+
+ first = MESA_SHADER_STAGES;
+ last = 0;
+
+ /* Determine first and last stage. Excluding the compute stage */
+ for (unsigned i = 0; i < MESA_SHADER_COMPUTE; i++) {
+ if (!prog->_LinkedShaders[i])
+ continue;
+ if (first == MESA_SHADER_STAGES)
+ first = i;
+ last = i;
+ }
+
+ if (first == MESA_SHADER_STAGES)
+ return;
+
+ for (unsigned stage = 0; stage < MESA_SHADER_STAGES; stage++) {
+ if (!prog->_LinkedShaders[stage])
+ continue;
+
+ /* Prevent the removal of inputs to the first and outputs from the last
+ * stage, unless they are the initial pipeline inputs or final pipeline
+ * outputs, respectively.
+ *
+ * The removal of IO between shaders in the same program is always
+ * allowed.
+ */
+ if (stage == first && stage != MESA_SHADER_VERTEX) {
+ set_always_active_io(prog->_LinkedShaders[stage]->Program->nir,
+ nir_var_shader_in);
+ }
+
+ if (stage == last && stage != MESA_SHADER_FRAGMENT) {
+ set_always_active_io(prog->_LinkedShaders[stage]->Program->nir,
+ nir_var_shader_out);
+ }
+ }
}
static bool
@@ -113,24 +547,26 @@ create_shader_variable(struct gl_shader_program *shProg,
*/
if (in->data.mode == nir_var_system_value &&
in->data.location == SYSTEM_VALUE_VERTEX_ID_ZERO_BASE) {
- out->name = ralloc_strdup(shProg, "gl_VertexID");
+ out->name.string = ralloc_strdup(shProg, "gl_VertexID");
} else if ((in->data.mode == nir_var_shader_out &&
in->data.location == VARYING_SLOT_TESS_LEVEL_OUTER) ||
(in->data.mode == nir_var_system_value &&
in->data.location == SYSTEM_VALUE_TESS_LEVEL_OUTER)) {
- out->name = ralloc_strdup(shProg, "gl_TessLevelOuter");
+ out->name.string = ralloc_strdup(shProg, "gl_TessLevelOuter");
type = glsl_array_type(glsl_float_type(), 4, 0);
} else if ((in->data.mode == nir_var_shader_out &&
in->data.location == VARYING_SLOT_TESS_LEVEL_INNER) ||
(in->data.mode == nir_var_system_value &&
in->data.location == SYSTEM_VALUE_TESS_LEVEL_INNER)) {
- out->name = ralloc_strdup(shProg, "gl_TessLevelInner");
+ out->name.string = ralloc_strdup(shProg, "gl_TessLevelInner");
type = glsl_array_type(glsl_float_type(), 2, 0);
} else {
- out->name = ralloc_strdup(shProg, name);
+ out->name.string = ralloc_strdup(shProg, name);
}
- if (!out->name)
+ resource_name_updated(&out->name);
+
+ if (!out->name.string)
return NULL;
/* The ARB_program_interface_query spec says:
@@ -171,7 +607,7 @@ create_shader_variable(struct gl_shader_program *shProg,
}
static bool
-add_shader_variable(const struct gl_context *ctx,
+add_shader_variable(const struct gl_constants *consts,
struct gl_shader_program *shProg,
struct set *resource_set,
unsigned stage_mask,
@@ -241,7 +677,7 @@ add_shader_variable(const struct gl_context *ctx,
glsl_get_struct_field_data(type, i);
char *field_name = ralloc_asprintf(shProg, "%s.%s", name, field->name);
- if (!add_shader_variable(ctx, shProg, resource_set,
+ if (!add_shader_variable(consts, shProg, resource_set,
stage_mask, programInterface,
var, field_name, field_type,
use_implicit_location, field_location,
@@ -277,7 +713,7 @@ add_shader_variable(const struct gl_context *ctx,
glsl_count_attribute_slots(array_type, false);
for (unsigned i = 0; i < glsl_get_length(type); i++) {
char *elem = ralloc_asprintf(shProg, "%s[%d]", name, i);
- if (!add_shader_variable(ctx, shProg, resource_set,
+ if (!add_shader_variable(consts, shProg, resource_set,
stage_mask, programInterface,
var, elem, array_type,
use_implicit_location, elem_location,
@@ -311,7 +747,7 @@ add_shader_variable(const struct gl_context *ctx,
}
static bool
-add_vars_with_modes(const struct gl_context *ctx,
+add_vars_with_modes(const struct gl_constants *consts,
struct gl_shader_program *prog, struct set *resource_set,
nir_shader *nir, nir_variable_mode modes,
unsigned stage, GLenum programInterface)
@@ -350,9 +786,11 @@ add_vars_with_modes(const struct gl_context *ctx,
* the linker needs to work without them. Returning them is optional.
* For simplicity, we ignore names.
*/
- sh_var->name = NULL;
+ sh_var->name.string = NULL;
+ resource_name_updated(&sh_var->name);
sh_var->type = var->type;
sh_var->location = var->data.location - loc_bias;
+ sh_var->explicit_location = var->data.explicit_location;
sh_var->index = var->data.index;
if (!link_util_add_program_resource(prog, resource_set,
@@ -376,7 +814,7 @@ add_vars_with_modes(const struct gl_context *ctx,
(stage == MESA_SHADER_FRAGMENT &&
var->data.mode == nir_var_shader_out);
- if (!add_shader_variable(ctx, prog, resource_set,
+ if (!add_shader_variable(consts, prog, resource_set,
1 << stage, programInterface,
var, var->name, var->type,
vs_input_or_fs_output,
@@ -391,7 +829,7 @@ add_vars_with_modes(const struct gl_context *ctx,
}
static bool
-add_interface_variables(const struct gl_context *ctx,
+add_interface_variables(const struct gl_constants *consts,
struct gl_shader_program *prog,
struct set *resource_set,
unsigned stage, GLenum programInterface)
@@ -405,12 +843,12 @@ add_interface_variables(const struct gl_context *ctx,
switch (programInterface) {
case GL_PROGRAM_INPUT: {
- return add_vars_with_modes(ctx, prog, resource_set,
+ return add_vars_with_modes(consts, prog, resource_set,
nir, nir_var_shader_in | nir_var_system_value,
stage, programInterface);
}
case GL_PROGRAM_OUTPUT:
- return add_vars_with_modes(ctx, prog, resource_set,
+ return add_vars_with_modes(consts, prog, resource_set,
nir, nir_var_shader_out,
stage, programInterface);
default:
@@ -421,21 +859,48 @@ add_interface_variables(const struct gl_context *ctx,
return false;
}
+bool
+nir_add_packed_var_to_resource_list(const struct gl_constants *consts,
+ struct gl_shader_program *shProg,
+ struct set *resource_set,
+ nir_variable *var,
+ unsigned stage, GLenum type)
+{
+ if (!add_shader_variable(consts, shProg, resource_set, 1 << stage,
+ type, var, var->name, var->type, false,
+ var->data.location - VARYING_SLOT_VAR0,
+ inout_has_same_location(var, stage), NULL))
+ return false;
+
+ return true;
+}
+
+/**
+ * Initilise list of program resources that point to resource data.
+ */
+void
+init_program_resource_list(struct gl_shader_program *prog)
+{
+ /* Rebuild resource list. */
+ if (prog->data->ProgramResourceList) {
+ ralloc_free(prog->data->ProgramResourceList);
+ prog->data->ProgramResourceList = NULL;
+ prog->data->NumProgramResourceList = 0;
+ }
+}
+
/* TODO: as we keep adding features, this method is becoming more and more
* similar to its GLSL counterpart at linker.cpp. Eventually it would be good
* to check if they could be refactored, and reduce code duplication somehow
*/
void
-nir_build_program_resource_list(struct gl_context *ctx,
+nir_build_program_resource_list(const struct gl_constants *consts,
struct gl_shader_program *prog,
bool rebuild_resourse_list)
{
/* Rebuild resource list. */
- if (prog->data->ProgramResourceList && rebuild_resourse_list) {
- ralloc_free(prog->data->ProgramResourceList);
- prog->data->ProgramResourceList = NULL;
- prog->data->NumProgramResourceList = 0;
- }
+ if (rebuild_resourse_list)
+ init_program_resource_list(prog);
int input_stage = MESA_SHADER_STAGES, output_stage = 0;
@@ -458,12 +923,12 @@ nir_build_program_resource_list(struct gl_context *ctx,
struct set *resource_set = _mesa_pointer_set_create(NULL);
/* Add inputs and outputs to the resource list. */
- if (!add_interface_variables(ctx, prog, resource_set, input_stage,
+ if (!add_interface_variables(consts, prog, resource_set, input_stage,
GL_PROGRAM_INPUT)) {
return;
}
- if (!add_interface_variables(ctx, prog, resource_set, output_stage,
+ if (!add_interface_variables(consts, prog, resource_set, output_stage,
GL_PROGRAM_OUTPUT)) {
return;
}
@@ -484,7 +949,7 @@ nir_build_program_resource_list(struct gl_context *ctx,
}
/* Add buffers. */
- for (unsigned i = 0; i < ctx->Const.MaxTransformFeedbackBuffers; i++) {
+ for (unsigned i = 0; i < consts->MaxTransformFeedbackBuffers; i++) {
if ((linked_xfb->ActiveBuffers >> i) & 1) {
linked_xfb->Buffers[i].Binding = i;
if (!link_util_add_program_resource(prog, resource_set,
@@ -596,44 +1061,1197 @@ nir_build_program_resource_list(struct gl_context *ctx,
_mesa_set_destroy(resource_set, NULL);
}
+static void
+shared_type_info(const struct glsl_type *type, unsigned *size, unsigned *align)
+{
+ assert(glsl_type_is_vector_or_scalar(type));
+
+ uint32_t comp_size = glsl_type_is_boolean(type)
+ ? 4 : glsl_get_bit_size(type) / 8;
+ unsigned length = glsl_get_vector_elements(type);
+ *size = comp_size * length,
+ *align = comp_size * (length == 3 ? 4 : length);
+}
+
+static bool
+can_remove_varying_before_linking(nir_variable *var, void *data)
+{
+ bool *is_sso = (bool *) data;
+ if (*is_sso) {
+ /* Allow the removal of unused builtins in SSO */
+ return var->data.location > -1 && var->data.location < VARYING_SLOT_VAR0;
+ } else
+ return true;
+}
+
+static void
+remove_dead_varyings_pre_linking(nir_shader *nir)
+{
+ struct nir_remove_dead_variables_options opts;
+ bool is_sso = nir->info.separate_shader;
+ opts.can_remove_var_data = &is_sso;
+ opts.can_remove_var = &can_remove_varying_before_linking;
+ nir_variable_mode mask = nir_var_shader_in | nir_var_shader_out;
+ nir_remove_dead_variables(nir, mask, &opts);
+}
+
+/* - create a gl_PointSize variable
+ * - find every gl_Position write
+ * - store 1.0 to gl_PointSize after every gl_Position write
+ */
+bool
+gl_nir_add_point_size(nir_shader *nir)
+{
+ nir_variable *psiz = nir_create_variable_with_location(nir, nir_var_shader_out,
+ VARYING_SLOT_PSIZ, glsl_float_type());
+ psiz->data.how_declared = nir_var_hidden;
+
+ nir_function_impl *impl = nir_shader_get_entrypoint(nir);
+ nir_builder b = nir_builder_create(impl);
+ bool found = false;
+ nir_foreach_block_safe(block, impl) {
+ nir_foreach_instr_safe(instr, block) {
+ if (instr->type == nir_instr_type_intrinsic) {
+ nir_intrinsic_instr *intr = nir_instr_as_intrinsic(instr);
+ if (intr->intrinsic == nir_intrinsic_store_deref ||
+ intr->intrinsic == nir_intrinsic_copy_deref) {
+ nir_variable *var = nir_intrinsic_get_var(intr, 0);
+ if (var->data.location == VARYING_SLOT_POS) {
+ b.cursor = nir_after_instr(instr);
+ nir_deref_instr *deref = nir_build_deref_var(&b, psiz);
+ nir_store_deref(&b, deref, nir_imm_float(&b, 1.0), BITFIELD_BIT(0));
+ found = true;
+ }
+ }
+ }
+ }
+ }
+ if (!found) {
+ b.cursor = nir_before_impl(impl);
+ nir_deref_instr *deref = nir_build_deref_var(&b, psiz);
+ nir_store_deref(&b, deref, nir_imm_float(&b, 1.0), BITFIELD_BIT(0));
+ }
+
+ nir->info.outputs_written |= VARYING_BIT_PSIZ;
+
+ /* We always modify the entrypoint */
+ nir_metadata_preserve(impl, nir_metadata_block_index | nir_metadata_dominance);
+ return true;
+}
+
+static void
+zero_array_members(nir_builder *b, nir_variable *var)
+{
+ nir_deref_instr *deref = nir_build_deref_var(b, var);
+ nir_def *zero = nir_imm_zero(b, 4, 32);
+ for (int i = 0; i < glsl_array_size(var->type); i++) {
+ nir_deref_instr *arr = nir_build_deref_array_imm(b, deref, i);
+ uint32_t mask = BITFIELD_MASK(glsl_get_vector_elements(arr->type));
+ nir_store_deref(b, arr, nir_channels(b, zero, mask), mask);
+ }
+}
+
+/* GL has an implicit default of 0 for unwritten gl_ClipDistance members;
+ * to achieve this, write 0 to all members at the start of the shader and
+ * let them be naturally overwritten later
+ */
+static bool
+gl_nir_zero_initialize_clip_distance(nir_shader *nir)
+{
+ nir_variable *clip_dist0 = nir_find_variable_with_location(nir, nir_var_shader_out, VARYING_SLOT_CLIP_DIST0);
+ nir_variable *clip_dist1 = nir_find_variable_with_location(nir, nir_var_shader_out, VARYING_SLOT_CLIP_DIST1);
+ if (!clip_dist0 && !clip_dist1)
+ return false;
+
+ nir_function_impl *impl = nir_shader_get_entrypoint(nir);
+ nir_builder b = nir_builder_at(nir_before_impl(impl));
+ if (clip_dist0)
+ zero_array_members(&b, clip_dist0);
+
+ if (clip_dist1)
+ zero_array_members(&b, clip_dist1);
+
+ nir_metadata_preserve(impl, nir_metadata_dominance |
+ nir_metadata_block_index);
+ return true;
+}
+
+static void
+lower_patch_vertices_in(struct gl_shader_program *shader_prog)
+{
+ struct gl_linked_shader *linked_tcs =
+ shader_prog->_LinkedShaders[MESA_SHADER_TESS_CTRL];
+ struct gl_linked_shader *linked_tes =
+ shader_prog->_LinkedShaders[MESA_SHADER_TESS_EVAL];
+
+ /* If we have a TCS and TES linked together, lower TES patch vertices. */
+ if (linked_tcs && linked_tes) {
+ nir_shader *tcs_nir = linked_tcs->Program->nir;
+ nir_shader *tes_nir = linked_tes->Program->nir;
+
+ /* The TES input vertex count is the TCS output vertex count,
+ * lower TES gl_PatchVerticesIn to a constant.
+ */
+ uint32_t tes_patch_verts = tcs_nir->info.tess.tcs_vertices_out;
+ NIR_PASS(_, tes_nir, nir_lower_patch_vertices, tes_patch_verts, NULL);
+ }
+}
+
+static void
+preprocess_shader(const struct gl_constants *consts,
+ const struct gl_extensions *exts,
+ struct gl_program *prog,
+ struct gl_shader_program *shader_program,
+ gl_shader_stage stage)
+{
+ const struct gl_shader_compiler_options *gl_options =
+ &consts->ShaderCompilerOptions[prog->info.stage];
+ const nir_shader_compiler_options *options = gl_options->NirOptions;
+ assert(options);
+
+ nir_shader *nir = prog->nir;
+
+ if (prog->info.stage == MESA_SHADER_FRAGMENT && consts->HasFBFetch) {
+ nir_shader_gather_info(prog->nir, nir_shader_get_entrypoint(prog->nir));
+ NIR_PASS(_, prog->nir, gl_nir_lower_blend_equation_advanced,
+ exts->KHR_blend_equation_advanced_coherent);
+ nir_lower_global_vars_to_local(prog->nir);
+ NIR_PASS(_, prog->nir, nir_opt_combine_stores, nir_var_shader_out);
+ }
+
+ /* Set the next shader stage hint for VS and TES. */
+ if (!nir->info.separate_shader &&
+ (nir->info.stage == MESA_SHADER_VERTEX ||
+ nir->info.stage == MESA_SHADER_TESS_EVAL)) {
+
+ unsigned prev_stages = (1 << (prog->info.stage + 1)) - 1;
+ unsigned stages_mask =
+ ~prev_stages & shader_program->data->linked_stages;
+
+ nir->info.next_stage = stages_mask ?
+ (gl_shader_stage) u_bit_scan(&stages_mask) : MESA_SHADER_FRAGMENT;
+ } else {
+ nir->info.next_stage = MESA_SHADER_FRAGMENT;
+ }
+
+ prog->skip_pointsize_xfb = !(nir->info.outputs_written & VARYING_BIT_PSIZ);
+ if (!consts->PointSizeFixed && prog->skip_pointsize_xfb &&
+ stage < MESA_SHADER_FRAGMENT && stage != MESA_SHADER_TESS_CTRL &&
+ gl_nir_can_add_pointsize_to_program(consts, prog)) {
+ NIR_PASS(_, nir, gl_nir_add_point_size);
+ }
+
+ if (stage < MESA_SHADER_FRAGMENT && stage != MESA_SHADER_TESS_CTRL &&
+ (nir->info.outputs_written & (VARYING_BIT_CLIP_DIST0 | VARYING_BIT_CLIP_DIST1)))
+ NIR_PASS(_, nir, gl_nir_zero_initialize_clip_distance);
+
+ if (options->lower_all_io_to_temps ||
+ nir->info.stage == MESA_SHADER_VERTEX ||
+ nir->info.stage == MESA_SHADER_GEOMETRY) {
+ NIR_PASS(_, nir, nir_lower_io_to_temporaries,
+ nir_shader_get_entrypoint(nir),
+ true, true);
+ } else if (nir->info.stage == MESA_SHADER_FRAGMENT ||
+ !consts->SupportsReadingOutputs) {
+ NIR_PASS(_, nir, nir_lower_io_to_temporaries,
+ nir_shader_get_entrypoint(nir),
+ true, false);
+ }
+
+ NIR_PASS(_, nir, nir_lower_global_vars_to_local);
+ NIR_PASS(_, nir, nir_split_var_copies);
+ NIR_PASS(_, nir, nir_lower_var_copies);
+
+ if (gl_options->LowerPrecisionFloat16 && gl_options->LowerPrecisionInt16) {
+ NIR_PASS(_, nir, nir_lower_mediump_vars, nir_var_function_temp | nir_var_shader_temp | nir_var_mem_shared);
+ }
+
+ if (options->lower_to_scalar) {
+ NIR_PASS(_, nir, nir_remove_dead_variables,
+ nir_var_function_temp | nir_var_shader_temp |
+ nir_var_mem_shared, NULL);
+ NIR_PASS(_, nir, nir_opt_copy_prop_vars);
+ NIR_PASS(_, nir, nir_lower_alu_to_scalar,
+ options->lower_to_scalar_filter, NULL);
+ }
+
+ NIR_PASS(_, nir, nir_opt_barrier_modes);
+
+ /* before buffers and vars_to_ssa */
+ NIR_PASS(_, nir, gl_nir_lower_images, true);
+
+ if (prog->nir->info.stage == MESA_SHADER_COMPUTE) {
+ NIR_PASS(_, prog->nir, nir_lower_vars_to_explicit_types,
+ nir_var_mem_shared, shared_type_info);
+ NIR_PASS(_, prog->nir, nir_lower_explicit_io,
+ nir_var_mem_shared, nir_address_format_32bit_offset);
+ }
+
+ /* Do a round of constant folding to clean up address calculations */
+ NIR_PASS(_, nir, nir_opt_constant_folding);
+}
+
+static bool
+prelink_lowering(const struct gl_constants *consts,
+ const struct gl_extensions *exts,
+ struct gl_shader_program *shader_program,
+ struct gl_linked_shader **linked_shader, unsigned num_shaders)
+{
+ for (unsigned i = 0; i < num_shaders; i++) {
+ struct gl_linked_shader *shader = linked_shader[i];
+ const nir_shader_compiler_options *options =
+ consts->ShaderCompilerOptions[shader->Stage].NirOptions;
+ struct gl_program *prog = shader->Program;
+
+ /* NIR drivers that support tess shaders and compact arrays need to use
+ * GLSLTessLevelsAsInputs / PIPE_CAP_GLSL_TESS_LEVELS_AS_INPUTS. The NIR
+ * linker doesn't support linking these as compat arrays of sysvals.
+ */
+ assert(consts->GLSLTessLevelsAsInputs || !options->compact_arrays ||
+ !exts->ARB_tessellation_shader);
+
+
+ /* ES 3.0+ vertex shaders may still have dead varyings but its now safe
+ * to remove them as validation is now done according to the spec.
+ */
+ if (shader_program->IsES && shader_program->GLSL_Version >= 300 &&
+ i == MESA_SHADER_VERTEX)
+ remove_dead_varyings_pre_linking(prog->nir);
+
+ preprocess_shader(consts, exts, prog, shader_program, shader->Stage);
+
+ if (prog->nir->info.shared_size > consts->MaxComputeSharedMemorySize) {
+ linker_error(shader_program, "Too much shared memory used (%u/%u)\n",
+ prog->nir->info.shared_size,
+ consts->MaxComputeSharedMemorySize);
+ return false;
+ }
+
+ if (options->lower_to_scalar) {
+ NIR_PASS(_, shader->Program->nir, nir_lower_load_const_to_scalar);
+ }
+ }
+
+ lower_patch_vertices_in(shader_program);
+
+ /* Linking shaders also optimizes them. Separate shaders, compute shaders
+ * and shaders with a fixed-func VS or FS that don't need linking are
+ * optimized here.
+ */
+ if (num_shaders == 1)
+ gl_nir_opts(linked_shader[0]->Program->nir);
+
+ /* nir_opt_access() needs to run before linking so that ImageAccess[]
+ * and BindlessImage[].access are filled out with the correct modes.
+ */
+ for (unsigned i = 0; i < num_shaders; i++) {
+ nir_shader *nir = linked_shader[i]->Program->nir;
+
+ nir_opt_access_options opt_access_options;
+ opt_access_options.is_vulkan = false;
+ NIR_PASS(_, nir, nir_opt_access, &opt_access_options);
+
+ if (!nir->options->compact_arrays) {
+ NIR_PASS(_, nir, nir_lower_clip_cull_distance_to_vec4s);
+ }
+
+ /* Combine clip and cull outputs into one array and set:
+ * - shader_info::clip_distance_array_size
+ * - shader_info::cull_distance_array_size
+ */
+ if (consts->CombinedClipCullDistanceArrays)
+ NIR_PASS(_, nir, nir_lower_clip_cull_distance_arrays);
+ }
+
+ return true;
+}
+
+/**
+ * Lower load_deref and store_deref on input/output variables to load_input
+ * and store_output intrinsics, and perform varying optimizations and
+ * compaction.
+ */
+void
+gl_nir_lower_optimize_varyings(const struct gl_constants *consts,
+ struct gl_shader_program *prog, bool spirv)
+{
+ nir_shader *shaders[MESA_SHADER_STAGES];
+ unsigned num_shaders = 0;
+ unsigned max_ubos = UINT_MAX;
+ unsigned max_uniform_comps = UINT_MAX;
+
+ for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
+ struct gl_linked_shader *shader = prog->_LinkedShaders[i];
+
+ if (!shader)
+ continue;
+
+ nir_shader *nir = shader->Program->nir;
+
+ if (nir->info.stage == MESA_SHADER_COMPUTE)
+ return;
+
+ if (!(nir->options->io_options & nir_io_glsl_lower_derefs) ||
+ !(nir->options->io_options & nir_io_glsl_opt_varyings))
+ return;
+
+ shaders[num_shaders] = nir;
+ max_uniform_comps = MIN2(max_uniform_comps,
+ consts->Program[i].MaxUniformComponents);
+ max_ubos = MIN2(max_ubos, consts->Program[i].MaxUniformBlocks);
+ num_shaders++;
+ }
+
+ /* Lower IO derefs to load and store intrinsics. */
+ for (unsigned i = 0; i < num_shaders; i++) {
+ nir_shader *nir = shaders[i];
+
+ nir_lower_io_passes(nir, true);
+ }
+
+ /* There is nothing to optimize for only 1 shader. */
+ if (num_shaders == 1)
+ return;
+
+ for (unsigned i = 0; i < num_shaders; i++) {
+ nir_shader *nir = shaders[i];
+
+ /* nir_opt_varyings requires scalar IO. */
+ NIR_PASS_V(nir, nir_lower_io_to_scalar,
+ (i != 0 ? nir_var_shader_in : 0) |
+ (i != num_shaders - 1 ? nir_var_shader_out : 0), NULL, NULL);
+
+ /* nir_opt_varyings requires shaders to be optimized. */
+ gl_nir_opts(nir);
+ }
+
+ /* Optimize varyings from the first shader to the last shader first, and
+ * then in the opposite order from the last changed producer.
+ *
+ * For example, VS->GS->FS is optimized in this order first:
+ * (VS,GS), (GS,FS)
+ *
+ * That ensures that constants and undefs (dead inputs) are propagated
+ * forward.
+ *
+ * If GS was changed while optimizing (GS,FS), (VS,GS) is optimized again
+ * because removing outputs in GS can cause a chain reaction in making
+ * GS inputs, VS outputs, and VS inputs dead.
+ */
+ unsigned highest_changed_producer = 0;
+ for (unsigned i = 0; i < num_shaders - 1; i++) {
+ nir_shader *producer = shaders[i];
+ nir_shader *consumer = shaders[i + 1];
+
+ nir_opt_varyings_progress progress =
+ nir_opt_varyings(producer, consumer, spirv, max_uniform_comps,
+ max_ubos);
+
+ if (progress & nir_progress_producer) {
+ gl_nir_opts(producer);
+ highest_changed_producer = i;
+ }
+ if (progress & nir_progress_consumer)
+ gl_nir_opts(consumer);
+ }
+
+ /* Optimize varyings from the highest changed producer to the first
+ * shader.
+ */
+ for (unsigned i = highest_changed_producer; i > 0; i--) {
+ nir_shader *producer = shaders[i - 1];
+ nir_shader *consumer = shaders[i];
+
+ nir_opt_varyings_progress progress =
+ nir_opt_varyings(producer, consumer, spirv, max_uniform_comps,
+ max_ubos);
+
+ if (progress & nir_progress_producer)
+ gl_nir_opts(producer);
+ if (progress & nir_progress_consumer)
+ gl_nir_opts(consumer);
+ }
+
+ /* Final cleanups. */
+ for (unsigned i = 0; i < num_shaders; i++) {
+ nir_shader *nir = shaders[i];
+
+ /* Recompute intrinsic bases, which are totally random after
+ * optimizations and compaction. Do that for all inputs and outputs,
+ * including VS inputs because those could have been removed too.
+ */
+ NIR_PASS_V(nir, nir_recompute_io_bases,
+ nir_var_shader_in | nir_var_shader_out);
+
+ /* Regenerate transform feedback info because compaction in
+ * nir_opt_varyings always moves them to other slots.
+ */
+ if (nir->xfb_info)
+ nir_gather_xfb_info_from_intrinsics(nir);
+ }
+}
+
bool
-gl_nir_link_spirv(struct gl_context *ctx, struct gl_shader_program *prog,
+gl_nir_link_spirv(const struct gl_constants *consts,
+ const struct gl_extensions *exts,
+ struct gl_shader_program *prog,
const struct gl_nir_linker_options *options)
{
+ struct gl_linked_shader *linked_shader[MESA_SHADER_STAGES];
+ unsigned num_shaders = 0;
+
+ MESA_TRACE_FUNC();
+
+ for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
+ if (prog->_LinkedShaders[i]) {
+ linked_shader[num_shaders++] = prog->_LinkedShaders[i];
+
+ remove_dead_varyings_pre_linking(prog->_LinkedShaders[i]->Program->nir);
+ }
+ }
+
+ if (!prelink_lowering(consts, exts, prog, linked_shader, num_shaders))
+ return false;
+
+ gl_nir_link_assign_xfb_resources(consts, prog);
+ gl_nir_lower_optimize_varyings(consts, prog, true);
+
+ if (!linked_shader[0]->Program->nir->info.io_lowered) {
+ /* Linking the stages in the opposite order (from fragment to vertex)
+ * ensures that inter-shader outputs written to in an earlier stage
+ * are eliminated if they are (transitively) not used in a later
+ * stage.
+ */
+ for (int i = num_shaders - 2; i >= 0; i--) {
+ gl_nir_link_opts(linked_shader[i]->Program->nir,
+ linked_shader[i + 1]->Program->nir);
+ }
+ }
+
for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
struct gl_linked_shader *shader = prog->_LinkedShaders[i];
if (shader) {
const nir_remove_dead_variables_options opts = {
- .can_remove_var = can_remove_uniform,
+ .can_remove_var = can_remove_var,
};
- nir_remove_dead_variables(shader->Program->nir, nir_var_uniform,
+ nir_remove_dead_variables(shader->Program->nir,
+ nir_var_uniform | nir_var_image,
&opts);
}
}
- if (!gl_nir_link_uniform_blocks(ctx, prog))
+ if (!gl_nir_link_uniform_blocks(consts, prog))
return false;
- if (!gl_nir_link_uniforms(ctx, prog, options->fill_parameters))
+ if (!gl_nir_link_uniforms(consts, prog, options->fill_parameters))
+ return false;
+
+ gl_nir_link_assign_atomic_counter_resources(consts, prog);
+
+ return true;
+}
+
+bool
+gl_nir_validate_intrastage_arrays(struct gl_shader_program *prog,
+ nir_variable *var, nir_variable *existing,
+ unsigned existing_stage,
+ bool match_precision)
+{
+ /* Consider the types to be "the same" if both types are arrays
+ * of the same type and one of the arrays is implicitly sized.
+ * In addition, set the type of the linked variable to the
+ * explicitly sized array.
+ */
+ if (glsl_type_is_array(var->type) && glsl_type_is_array(existing->type)) {
+ const glsl_type *no_array_var = glsl_get_array_element(var->type);
+ const glsl_type *no_array_existing =
+ glsl_get_array_element(existing->type);
+ bool type_matches;
+
+ type_matches = (match_precision ?
+ no_array_var == no_array_existing :
+ glsl_type_compare_no_precision(no_array_var, no_array_existing));
+
+ if (type_matches &&
+ ((glsl_array_size(var->type) == 0) ||
+ (glsl_array_size(existing->type) == 0))) {
+ if (glsl_array_size(var->type) != 0) {
+ if ((int)glsl_array_size(var->type) <=
+ existing->data.max_array_access) {
+ linker_error(prog, "%s `%s' declared as type "
+ "`%s' but outermost dimension has an index"
+ " of `%i'\n",
+ gl_nir_mode_string(var),
+ var->name, glsl_get_type_name(var->type),
+ existing->data.max_array_access);
+ }
+ existing->type = var->type;
+
+ nir_shader *s = prog->_LinkedShaders[existing_stage]->Program->nir;
+ nir_fixup_deref_types(s);
+ return true;
+ } else if (glsl_array_size(existing->type) != 0) {
+ if((int)glsl_array_size(existing->type) <= var->data.max_array_access &&
+ !existing->data.from_ssbo_unsized_array) {
+ linker_error(prog, "%s `%s' declared as type "
+ "`%s' but outermost dimension has an index"
+ " of `%i'\n",
+ gl_nir_mode_string(var),
+ var->name, glsl_get_type_name(existing->type),
+ var->data.max_array_access);
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static bool
+nir_constant_compare(const nir_constant *c1, const nir_constant *c2)
+{
+ bool match = true;
+
+ match &= memcmp(c1->values, c2->values, sizeof(c1->values)) == 0;
+ match &= c1->is_null_constant == c2->is_null_constant;
+ match &= c1->num_elements == c2->num_elements;
+ if (!match)
return false;
- gl_nir_link_assign_atomic_counter_resources(ctx, prog);
- gl_nir_link_assign_xfb_resources(ctx, prog);
+ for (unsigned i = 0; i < c1->num_elements; i++) {
+ match &= nir_constant_compare(c1->elements[i], c2->elements[i]);
+ }
+
+ return match;
+}
+
+struct ifc_var {
+ unsigned stage;
+ nir_variable *var;
+};
+
+/**
+ * Perform validation of global variables used across multiple shaders
+ */
+static void
+cross_validate_globals(void *mem_ctx, const struct gl_constants *consts,
+ struct gl_shader_program *prog,
+ nir_shader *shader, struct hash_table *variables,
+ bool uniforms_only)
+{
+ nir_foreach_variable_in_shader(var, shader) {
+ if (uniforms_only &&
+ (var->data.mode != nir_var_uniform &&
+ var->data.mode != nir_var_mem_ubo &&
+ var->data.mode != nir_var_image &&
+ var->data.mode != nir_var_mem_ssbo))
+ continue;
+
+ /* don't cross validate subroutine uniforms */
+ if (glsl_contains_subroutine(var->type))
+ continue;
+
+ /* Don't cross validate interface instances. These are only relevant
+ * inside a shader. The cross validation is done at the Interface Block
+ * name level.
+ */
+ if (glsl_without_array(var->type) == var->interface_type)
+ continue;
+
+ /* Don't cross validate temporaries that are at global scope. These
+ * will eventually get pulled into the shaders 'main'.
+ */
+ if (var->data.mode == nir_var_function_temp ||
+ var->data.mode == nir_var_shader_temp)
+ continue;
+
+ /* If a global with this name has already been seen, verify that the
+ * new instance has the same type. In addition, if the globals have
+ * initializers, the values of the initializers must be the same.
+ */
+ struct hash_entry *entry =
+ _mesa_hash_table_search(variables, var->name);
+ if (entry != NULL) {
+ struct ifc_var *existing_ifc = (struct ifc_var *) entry->data;
+ nir_variable *existing = existing_ifc->var;
+
+ /* Check if types match. */
+ if (var->type != existing->type) {
+ if (!gl_nir_validate_intrastage_arrays(prog, var, existing,
+ existing_ifc->stage, true)) {
+ /* If it is an unsized array in a Shader Storage Block,
+ * two different shaders can access to different elements.
+ * Because of that, they might be converted to different
+ * sized arrays, then check that they are compatible but
+ * ignore the array size.
+ */
+ if (!(var->data.mode == nir_var_mem_ssbo &&
+ var->data.from_ssbo_unsized_array &&
+ existing->data.mode == nir_var_mem_ssbo &&
+ existing->data.from_ssbo_unsized_array &&
+ glsl_get_gl_type(var->type) == glsl_get_gl_type(existing->type))) {
+ linker_error(prog, "%s `%s' declared as type "
+ "`%s' and type `%s'\n",
+ gl_nir_mode_string(var),
+ var->name, glsl_get_type_name(var->type),
+ glsl_get_type_name(existing->type));
+ return;
+ }
+ }
+ }
+
+ if (var->data.explicit_location) {
+ if (existing->data.explicit_location
+ && (var->data.location != existing->data.location)) {
+ linker_error(prog, "explicit locations for %s "
+ "`%s' have differing values\n",
+ gl_nir_mode_string(var), var->name);
+ return;
+ }
+
+ if (var->data.location_frac != existing->data.location_frac) {
+ linker_error(prog, "explicit components for %s `%s' have "
+ "differing values\n", gl_nir_mode_string(var),
+ var->name);
+ return;
+ }
+
+ existing->data.location = var->data.location;
+ existing->data.explicit_location = true;
+ } else {
+ /* Check if uniform with implicit location was marked explicit
+ * by earlier shader stage. If so, mark it explicit in this stage
+ * too to make sure later processing does not treat it as
+ * implicit one.
+ */
+ if (existing->data.explicit_location) {
+ var->data.location = existing->data.location;
+ var->data.explicit_location = true;
+ }
+ }
+
+ /* From the GLSL 4.20 specification:
+ * "A link error will result if two compilation units in a program
+ * specify different integer-constant bindings for the same
+ * opaque-uniform name. However, it is not an error to specify a
+ * binding on some but not all declarations for the same name"
+ */
+ if (var->data.explicit_binding) {
+ if (existing->data.explicit_binding &&
+ var->data.binding != existing->data.binding) {
+ linker_error(prog, "explicit bindings for %s "
+ "`%s' have differing values\n",
+ gl_nir_mode_string(var), var->name);
+ return;
+ }
+
+ existing->data.binding = var->data.binding;
+ existing->data.explicit_binding = true;
+ }
+
+ if (glsl_contains_atomic(var->type) &&
+ var->data.offset != existing->data.offset) {
+ linker_error(prog, "offset specifications for %s "
+ "`%s' have differing values\n",
+ gl_nir_mode_string(var), var->name);
+ return;
+ }
+
+ /* Validate layout qualifiers for gl_FragDepth.
+ *
+ * From the AMD/ARB_conservative_depth specs:
+ *
+ * "If gl_FragDepth is redeclared in any fragment shader in a
+ * program, it must be redeclared in all fragment shaders in
+ * that program that have static assignments to
+ * gl_FragDepth. All redeclarations of gl_FragDepth in all
+ * fragment shaders in a single program must have the same set
+ * of qualifiers."
+ */
+ if (strcmp(var->name, "gl_FragDepth") == 0) {
+ bool layout_declared = var->data.depth_layout != nir_depth_layout_none;
+ bool layout_differs =
+ var->data.depth_layout != existing->data.depth_layout;
+
+ if (layout_declared && layout_differs) {
+ linker_error(prog,
+ "All redeclarations of gl_FragDepth in all "
+ "fragment shaders in a single program must have "
+ "the same set of qualifiers.\n");
+ }
+
+ if (var->data.used && layout_differs) {
+ linker_error(prog,
+ "If gl_FragDepth is redeclared with a layout "
+ "qualifier in any fragment shader, it must be "
+ "redeclared with the same layout qualifier in "
+ "all fragment shaders that have assignments to "
+ "gl_FragDepth\n");
+ }
+ }
+
+ /* Page 35 (page 41 of the PDF) of the GLSL 4.20 spec says:
+ *
+ * "If a shared global has multiple initializers, the
+ * initializers must all be constant expressions, and they
+ * must all have the same value. Otherwise, a link error will
+ * result. (A shared global having only one initializer does
+ * not require that initializer to be a constant expression.)"
+ *
+ * Previous to 4.20 the GLSL spec simply said that initializers
+ * must have the same value. In this case of non-constant
+ * initializers, this was impossible to determine. As a result,
+ * no vendor actually implemented that behavior. The 4.20
+ * behavior matches the implemented behavior of at least one other
+ * vendor, so we'll implement that for all GLSL versions.
+ * If (at least) one of these constant expressions is implicit,
+ * because it was added by glsl_zero_init, we skip the verification.
+ */
+ if (var->constant_initializer != NULL) {
+ if (existing->constant_initializer != NULL &&
+ !existing->data.is_implicit_initializer &&
+ !var->data.is_implicit_initializer) {
+ if (!nir_constant_compare(var->constant_initializer,
+ existing->constant_initializer)) {
+ linker_error(prog, "initializers for %s "
+ "`%s' have differing values\n",
+ gl_nir_mode_string(var), var->name);
+ return;
+ }
+ } else {
+ /* If the first-seen instance of a particular uniform did
+ * not have an initializer but a later instance does,
+ * replace the former with the later.
+ */
+ if (!var->data.is_implicit_initializer)
+ _mesa_hash_table_insert(variables, existing->name, var);
+ }
+ }
+
+ if (var->data.has_initializer) {
+ if (existing->data.has_initializer
+ && (var->constant_initializer == NULL
+ || existing->constant_initializer == NULL)) {
+ linker_error(prog,
+ "shared global variable `%s' has multiple "
+ "non-constant initializers.\n",
+ var->name);
+ return;
+ }
+ }
+
+ if (existing->data.explicit_invariant != var->data.explicit_invariant) {
+ linker_error(prog, "declarations for %s `%s' have "
+ "mismatching invariant qualifiers\n",
+ gl_nir_mode_string(var), var->name);
+ return;
+ }
+ if (existing->data.centroid != var->data.centroid) {
+ linker_error(prog, "declarations for %s `%s' have "
+ "mismatching centroid qualifiers\n",
+ gl_nir_mode_string(var), var->name);
+ return;
+ }
+ if (existing->data.sample != var->data.sample) {
+ linker_error(prog, "declarations for %s `%s` have "
+ "mismatching sample qualifiers\n",
+ gl_nir_mode_string(var), var->name);
+ return;
+ }
+ if (existing->data.image.format != var->data.image.format) {
+ linker_error(prog, "declarations for %s `%s` have "
+ "mismatching image format qualifiers\n",
+ gl_nir_mode_string(var), var->name);
+ return;
+ }
+
+ /* Check the precision qualifier matches for uniform variables on
+ * GLSL ES.
+ */
+ if (!consts->AllowGLSLRelaxedES &&
+ prog->IsES && !var->interface_type &&
+ existing->data.precision != var->data.precision) {
+ if ((existing->data.used && var->data.used) ||
+ prog->GLSL_Version >= 300) {
+ linker_error(prog, "declarations for %s `%s` have "
+ "mismatching precision qualifiers\n",
+ gl_nir_mode_string(var), var->name);
+ return;
+ } else {
+ linker_warning(prog, "declarations for %s `%s` have "
+ "mismatching precision qualifiers\n",
+ gl_nir_mode_string(var), var->name);
+ }
+ }
+
+ /* In OpenGL GLSL 3.20 spec, section 4.3.9:
+ *
+ * "It is a link-time error if any particular shader interface
+ * contains:
+ *
+ * - two different blocks, each having no instance name, and each
+ * having a member of the same name, or
+ *
+ * - a variable outside a block, and a block with no instance name,
+ * where the variable has the same name as a member in the block."
+ */
+ const glsl_type *var_itype = var->interface_type;
+ const glsl_type *existing_itype = existing->interface_type;
+ if (var_itype != existing_itype) {
+ if (!var_itype || !existing_itype) {
+ linker_error(prog, "declarations for %s `%s` are inside block "
+ "`%s` and outside a block",
+ gl_nir_mode_string(var), var->name,
+ glsl_get_type_name(var_itype ? var_itype : existing_itype));
+ return;
+ } else if (strcmp(glsl_get_type_name(var_itype), glsl_get_type_name(existing_itype)) != 0) {
+ linker_error(prog, "declarations for %s `%s` are inside blocks "
+ "`%s` and `%s`",
+ gl_nir_mode_string(var), var->name,
+ glsl_get_type_name(existing_itype),
+ glsl_get_type_name(var_itype));
+ return;
+ }
+ }
+ } else {
+ struct ifc_var *ifc_var = ralloc(mem_ctx, struct ifc_var);
+ ifc_var->var = var;
+ ifc_var->stage = shader->info.stage;
+ _mesa_hash_table_insert(variables, var->name, ifc_var);
+ }
+ }
+}
+
+/**
+ * Perform validation of uniforms used across multiple shader stages
+ */
+static void
+cross_validate_uniforms(const struct gl_constants *consts,
+ struct gl_shader_program *prog)
+{
+ void *mem_ctx = ralloc_context(NULL);
+ struct hash_table *variables =
+ _mesa_hash_table_create(mem_ctx, _mesa_hash_string, _mesa_key_string_equal);
+ for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
+ if (prog->_LinkedShaders[i] == NULL)
+ continue;
+
+ cross_validate_globals(mem_ctx, consts, prog,
+ prog->_LinkedShaders[i]->Program->nir,
+ variables, true);
+ }
+
+ ralloc_free(mem_ctx);
+}
+
+/**
+ * Initializes explicit location slots to INACTIVE_UNIFORM_EXPLICIT_LOCATION
+ * for a variable, checks for overlaps between other uniforms using explicit
+ * locations.
+ */
+static int
+reserve_explicit_locations(struct gl_shader_program *prog,
+ struct string_to_uint_map *map, nir_variable *var)
+{
+ unsigned slots = glsl_type_uniform_locations(var->type);
+ unsigned max_loc = var->data.location + slots - 1;
+ unsigned return_value = slots;
+
+ /* Resize remap table if locations do not fit in the current one. */
+ if (max_loc + 1 > prog->NumUniformRemapTable) {
+ prog->UniformRemapTable =
+ reralloc(prog, prog->UniformRemapTable,
+ struct gl_uniform_storage *,
+ max_loc + 1);
+
+ if (!prog->UniformRemapTable) {
+ linker_error(prog, "Out of memory during linking.\n");
+ return -1;
+ }
+
+ /* Initialize allocated space. */
+ for (unsigned i = prog->NumUniformRemapTable; i < max_loc + 1; i++)
+ prog->UniformRemapTable[i] = NULL;
+
+ prog->NumUniformRemapTable = max_loc + 1;
+ }
+
+ for (unsigned i = 0; i < slots; i++) {
+ unsigned loc = var->data.location + i;
+
+ /* Check if location is already used. */
+ if (prog->UniformRemapTable[loc] == INACTIVE_UNIFORM_EXPLICIT_LOCATION) {
+
+ /* Possibly same uniform from a different stage, this is ok. */
+ unsigned hash_loc;
+ if (string_to_uint_map_get(map, &hash_loc, var->name) &&
+ hash_loc == loc - i) {
+ return_value = 0;
+ continue;
+ }
+
+ /* ARB_explicit_uniform_location specification states:
+ *
+ * "No two default-block uniform variables in the program can have
+ * the same location, even if they are unused, otherwise a compiler
+ * or linker error will be generated."
+ */
+ linker_error(prog,
+ "location qualifier for uniform %s overlaps "
+ "previously used location\n",
+ var->name);
+ return -1;
+ }
+
+ /* Initialize location as inactive before optimization
+ * rounds and location assignment.
+ */
+ prog->UniformRemapTable[loc] = INACTIVE_UNIFORM_EXPLICIT_LOCATION;
+ }
+
+ /* Note, base location used for arrays. */
+ string_to_uint_map_put(map, var->data.location, var->name);
+
+ return return_value;
+}
+
+static bool
+reserve_subroutine_explicit_locations(struct gl_shader_program *prog,
+ struct gl_program *p,
+ nir_variable *var)
+{
+ unsigned slots = glsl_type_uniform_locations(var->type);
+ unsigned max_loc = var->data.location + slots - 1;
+
+ /* Resize remap table if locations do not fit in the current one. */
+ if (max_loc + 1 > p->sh.NumSubroutineUniformRemapTable) {
+ p->sh.SubroutineUniformRemapTable =
+ reralloc(p, p->sh.SubroutineUniformRemapTable,
+ struct gl_uniform_storage *,
+ max_loc + 1);
+
+ if (!p->sh.SubroutineUniformRemapTable) {
+ linker_error(prog, "Out of memory during linking.\n");
+ return false;
+ }
+
+ /* Initialize allocated space. */
+ for (unsigned i = p->sh.NumSubroutineUniformRemapTable; i < max_loc + 1; i++)
+ p->sh.SubroutineUniformRemapTable[i] = NULL;
+
+ p->sh.NumSubroutineUniformRemapTable = max_loc + 1;
+ }
+
+ for (unsigned i = 0; i < slots; i++) {
+ unsigned loc = var->data.location + i;
+
+ /* Check if location is already used. */
+ if (p->sh.SubroutineUniformRemapTable[loc] == INACTIVE_UNIFORM_EXPLICIT_LOCATION) {
+
+ /* ARB_explicit_uniform_location specification states:
+ * "No two subroutine uniform variables can have the same location
+ * in the same shader stage, otherwise a compiler or linker error
+ * will be generated."
+ */
+ linker_error(prog,
+ "location qualifier for uniform %s overlaps "
+ "previously used location\n",
+ var->name);
+ return false;
+ }
+
+ /* Initialize location as inactive before optimization
+ * rounds and location assignment.
+ */
+ p->sh.SubroutineUniformRemapTable[loc] = INACTIVE_UNIFORM_EXPLICIT_LOCATION;
+ }
return true;
}
+/**
+ * Check and reserve all explicit uniform locations, called before
+ * any optimizations happen to handle also inactive uniforms and
+ * inactive array elements that may get trimmed away.
+ */
+static void
+check_explicit_uniform_locations(const struct gl_extensions *exts,
+ struct gl_shader_program *prog)
+{
+ prog->NumExplicitUniformLocations = 0;
+
+ if (!exts->ARB_explicit_uniform_location)
+ return;
+
+ /* This map is used to detect if overlapping explicit locations
+ * occur with the same uniform (from different stage) or a different one.
+ */
+ struct string_to_uint_map *uniform_map = string_to_uint_map_ctor();
+
+ if (!uniform_map) {
+ linker_error(prog, "Out of memory during linking.\n");
+ return;
+ }
+
+ unsigned entries_total = 0;
+ unsigned mask = prog->data->linked_stages;
+ while (mask) {
+ const int i = u_bit_scan(&mask);
+ struct gl_program *p = prog->_LinkedShaders[i]->Program;
+
+ unsigned modes = nir_var_uniform | nir_var_mem_ubo | nir_var_image;
+ nir_foreach_variable_with_modes(var, p->nir, modes) {
+ if (var->data.explicit_location) {
+ bool ret = false;
+ if (glsl_type_is_subroutine(glsl_without_array(var->type)))
+ ret = reserve_subroutine_explicit_locations(prog, p, var);
+ else {
+ int slots = reserve_explicit_locations(prog, uniform_map,
+ var);
+ if (slots != -1) {
+ ret = true;
+ entries_total += slots;
+ }
+ }
+ if (!ret) {
+ string_to_uint_map_dtor(uniform_map);
+ return;
+ }
+ }
+ }
+ }
+
+ link_util_update_empty_uniform_locations(prog);
+
+ string_to_uint_map_dtor(uniform_map);
+ prog->NumExplicitUniformLocations = entries_total;
+}
+
+static void
+link_assign_subroutine_types(struct gl_shader_program *prog)
+{
+ unsigned mask = prog->data->linked_stages;
+ while (mask) {
+ const int i = u_bit_scan(&mask);
+ struct gl_program *p = prog->_LinkedShaders[i]->Program;
+
+ struct set *fn_decl_set =
+ _mesa_set_create(NULL, _mesa_hash_string, _mesa_key_string_equal);
+
+ p->sh.MaxSubroutineFunctionIndex = 0;
+ nir_foreach_function(fn, p->nir) {
+ /* A function might be decalred multiple times but we should only
+ * process it once
+ */
+ struct set_entry *entry = _mesa_set_search(fn_decl_set, fn->name);
+ if (entry)
+ continue;
+
+ _mesa_set_add(fn_decl_set, fn->name);
+
+ if (fn->is_subroutine)
+ p->sh.NumSubroutineUniformTypes++;
+
+ if (!fn->num_subroutine_types)
+ continue;
+
+ /* these should have been calculated earlier. */
+ assert(fn->subroutine_index != -1);
+ if (p->sh.NumSubroutineFunctions + 1 > MAX_SUBROUTINES) {
+ linker_error(prog, "Too many subroutine functions declared.\n");
+ return;
+ }
+ p->sh.SubroutineFunctions = reralloc(p, p->sh.SubroutineFunctions,
+ struct gl_subroutine_function,
+ p->sh.NumSubroutineFunctions + 1);
+ p->sh.SubroutineFunctions[p->sh.NumSubroutineFunctions].name.string = ralloc_strdup(p, fn->name);
+ resource_name_updated(&p->sh.SubroutineFunctions[p->sh.NumSubroutineFunctions].name);
+ p->sh.SubroutineFunctions[p->sh.NumSubroutineFunctions].num_compat_types = fn->num_subroutine_types;
+ p->sh.SubroutineFunctions[p->sh.NumSubroutineFunctions].types =
+ ralloc_array(p, const struct glsl_type *,
+ fn->num_subroutine_types);
+
+ /* From Section 4.4.4(Subroutine Function Layout Qualifiers) of the
+ * GLSL 4.5 spec:
+ *
+ * "Each subroutine with an index qualifier in the shader must be
+ * given a unique index, otherwise a compile or link error will be
+ * generated."
+ */
+ for (unsigned j = 0; j < p->sh.NumSubroutineFunctions; j++) {
+ if (p->sh.SubroutineFunctions[j].index != -1 &&
+ p->sh.SubroutineFunctions[j].index == fn->subroutine_index) {
+ linker_error(prog, "each subroutine index qualifier in the "
+ "shader must be unique\n");
+ return;
+ }
+ }
+ p->sh.SubroutineFunctions[p->sh.NumSubroutineFunctions].index =
+ fn->subroutine_index;
+
+ if (fn->subroutine_index > (int)p->sh.MaxSubroutineFunctionIndex)
+ p->sh.MaxSubroutineFunctionIndex = fn->subroutine_index;
+
+ for (int j = 0; j < fn->num_subroutine_types; j++)
+ p->sh.SubroutineFunctions[p->sh.NumSubroutineFunctions].types[j] = fn->subroutine_types[j];
+ p->sh.NumSubroutineFunctions++;
+ }
+
+ _mesa_set_destroy(fn_decl_set, NULL);
+ }
+}
+
+static void
+verify_subroutine_associated_funcs(struct gl_shader_program *prog)
+{
+ unsigned mask = prog->data->linked_stages;
+ while (mask) {
+ const int i = u_bit_scan(&mask);
+ struct gl_program *p = prog->_LinkedShaders[i]->Program;
+
+ /* Section 6.1.2 (Subroutines) of the GLSL 4.00 spec says:
+ *
+ * "A program will fail to compile or link if any shader
+ * or stage contains two or more functions with the same
+ * name if the name is associated with a subroutine type."
+ */
+ for (unsigned j = 0; j < p->sh.NumSubroutineFunctions; j++) {
+ unsigned definitions = 0;
+ char *name = p->sh.SubroutineFunctions[j].name.string;
+
+ /* Calculate number of function definitions with the same name */
+ nir_foreach_function(fn, p->nir) {
+ /* If the function is only declared not implemented continue */
+ if (fn->impl != NULL)
+ continue;
+
+ if (strcmp(fn->name, name) == 0) {
+ if (++definitions > 1) {
+ linker_error(prog, "%s shader contains two or more function "
+ "definitions with name `%s', which is "
+ "associated with a subroutine type.\n",
+ _mesa_shader_stage_to_string(i),
+ fn->name);
+ return;
+ }
+ }
+ }
+ }
+ }
+}
/**
* Validate shader image resources.
*/
static void
-check_image_resources(struct gl_context *ctx, struct gl_shader_program *prog)
+check_image_resources(const struct gl_constants *consts,
+ const struct gl_extensions *exts,
+ struct gl_shader_program *prog)
{
unsigned total_image_units = 0;
unsigned fragment_outputs = 0;
unsigned total_shader_storage_blocks = 0;
- if (!ctx->Extensions.ARB_shader_image_load_store)
+ if (!exts->ARB_shader_image_load_store)
return;
for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
@@ -645,7 +2263,7 @@ check_image_resources(struct gl_context *ctx, struct gl_shader_program *prog)
total_shader_storage_blocks += sh->Program->info.num_ssbos;
}
- if (total_image_units > ctx->Const.MaxCombinedImageUniforms)
+ if (total_image_units > consts->MaxCombinedImageUniforms)
linker_error(prog, "Too many combined image uniforms\n");
struct gl_linked_shader *frag_sh =
@@ -656,34 +2274,760 @@ check_image_resources(struct gl_context *ctx, struct gl_shader_program *prog)
}
if (total_image_units + fragment_outputs + total_shader_storage_blocks >
- ctx->Const.MaxCombinedShaderOutputResources)
+ consts->MaxCombinedShaderOutputResources)
linker_error(prog, "Too many combined image uniforms, shader storage "
" buffers and fragment outputs\n");
}
+static bool
+is_sampler_array_accessed_indirectly(nir_deref_instr *deref)
+{
+ for (nir_deref_instr *d = deref; d; d = nir_deref_instr_parent(d)) {
+ if (d->deref_type != nir_deref_type_array)
+ continue;
+
+ if (nir_src_is_const(d->arr.index))
+ continue;
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * This check is done to make sure we allow only constant expression
+ * indexing and "constant-index-expression" (indexing with an expression
+ * that includes loop induction variable).
+ */
+static bool
+validate_sampler_array_indexing(const struct gl_constants *consts,
+ struct gl_shader_program *prog)
+{
+ for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
+ if (prog->_LinkedShaders[i] == NULL)
+ continue;
+
+ bool no_dynamic_indexing =
+ consts->ShaderCompilerOptions[i].NirOptions->force_indirect_unrolling_sampler;
+
+ bool uses_indirect_sampler_array_indexing = false;
+ nir_foreach_function_impl(impl, prog->_LinkedShaders[i]->Program->nir) {
+ nir_foreach_block(block, impl) {
+ nir_foreach_instr(instr, block) {
+ /* Check if a sampler array is accessed indirectly */
+ if (instr->type == nir_instr_type_tex) {
+ nir_tex_instr *tex_instr = nir_instr_as_tex(instr);
+ int sampler_idx =
+ nir_tex_instr_src_index(tex_instr, nir_tex_src_sampler_deref);
+ if (sampler_idx >= 0) {
+ nir_deref_instr *deref =
+ nir_instr_as_deref(tex_instr->src[sampler_idx].src.ssa->parent_instr);
+ if (is_sampler_array_accessed_indirectly(deref)) {
+ uses_indirect_sampler_array_indexing = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (uses_indirect_sampler_array_indexing)
+ break;
+ }
+ if (uses_indirect_sampler_array_indexing)
+ break;
+ }
+
+ if (uses_indirect_sampler_array_indexing) {
+ const char *msg = "sampler arrays indexed with non-constant "
+ "expressions is forbidden in GLSL %s %u";
+ /* Backend has indicated that it has no dynamic indexing support. */
+ if (no_dynamic_indexing) {
+ linker_error(prog, msg, prog->IsES ? "ES" : "", prog->GLSL_Version);
+ return false;
+ } else {
+ linker_warning(prog, msg, prog->IsES ? "ES" : "",
+ prog->GLSL_Version);
+ }
+ }
+ }
+
+ return true;
+}
+
+static nir_variable *
+find_frag_builtin(nir_shader *shader, bool is_sysval, unsigned sysval,
+ unsigned varying)
+{
+
+ unsigned location = is_sysval ? sysval : varying;
+ nir_variable_mode mode =
+ is_sysval ? nir_var_system_value : nir_var_shader_in;
+
+ return nir_find_variable_with_location(shader, mode, location);
+}
+
+/**
+ * Verifies the invariance of built-in special variables.
+ */
+static bool
+validate_invariant_builtins(const struct gl_constants *consts,
+ struct gl_shader_program *prog,
+ const struct gl_linked_shader *vert,
+ const struct gl_linked_shader *frag)
+{
+ const nir_variable *var_vert;
+ const nir_variable *var_frag;
+
+ if (!vert || !frag)
+ return true;
+
+ /*
+ * From OpenGL ES Shading Language 1.0 specification
+ * (4.6.4 Invariance and Linkage):
+ * "The invariance of varyings that are declared in both the vertex and
+ * fragment shaders must match. For the built-in special variables,
+ * gl_FragCoord can only be declared invariant if and only if
+ * gl_Position is declared invariant. Similarly gl_PointCoord can only
+ * be declared invariant if and only if gl_PointSize is declared
+ * invariant. It is an error to declare gl_FrontFacing as invariant.
+ * The invariance of gl_FrontFacing is the same as the invariance of
+ * gl_Position."
+ */
+ var_frag = find_frag_builtin(frag->Program->nir,
+ consts->GLSLFragCoordIsSysVal,
+ SYSTEM_VALUE_FRAG_COORD, VARYING_SLOT_POS);
+ if (var_frag && var_frag->data.invariant) {
+ var_vert = nir_find_variable_with_location(vert->Program->nir,
+ nir_var_shader_out,
+ VARYING_SLOT_POS);
+ if (var_vert && !var_vert->data.invariant) {
+ linker_error(prog,
+ "fragment shader built-in `%s' has invariant qualifier, "
+ "but vertex shader built-in `%s' lacks invariant qualifier\n",
+ var_frag->name, var_vert->name);
+ return false;
+ }
+ }
+
+ var_frag = find_frag_builtin(frag->Program->nir,
+ consts->GLSLPointCoordIsSysVal,
+ SYSTEM_VALUE_POINT_COORD, VARYING_SLOT_PNTC);
+ if (var_frag && var_frag->data.invariant) {
+ var_vert = nir_find_variable_with_location(vert->Program->nir,
+ nir_var_shader_out,
+ VARYING_SLOT_PSIZ);
+ if (var_vert && !var_vert->data.invariant) {
+ linker_error(prog,
+ "fragment shader built-in `%s' has invariant qualifier, "
+ "but vertex shader built-in `%s' lacks invariant qualifier\n",
+ var_frag->name, var_vert->name);
+ return false;
+ }
+ }
+
+ var_frag = find_frag_builtin(frag->Program->nir,
+ consts->GLSLFrontFacingIsSysVal,
+ SYSTEM_VALUE_FRONT_FACE, VARYING_SLOT_FACE);
+ if (var_frag && var_frag->data.invariant) {
+ linker_error(prog,
+ "fragment shader built-in `%s' can not be declared as invariant\n",
+ var_frag->name);
+ return false;
+ }
+
+ return true;
+}
+
+static void
+find_assignments(nir_shader *shader, nir_variable *var1, nir_variable *var2,
+ nir_variable *var3, bool *var1_written, bool *var2_written,
+ bool *var3_written)
+{
+ nir_foreach_function_impl(impl, shader) {
+ nir_foreach_block(block, impl) {
+ nir_foreach_instr(instr, block) {
+ if (instr->type == nir_instr_type_intrinsic) {
+ nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr);
+ if (intrin->intrinsic == nir_intrinsic_store_deref ||
+ intrin->intrinsic == nir_intrinsic_copy_deref) {
+ nir_deref_instr *deref = nir_src_as_deref(intrin->src[0]);
+ nir_variable *var = nir_deref_instr_get_variable(deref);
+ if (!var)
+ continue;
+
+ if (var == var1)
+ *var1_written = true;
+ else if (var == var2)
+ *var2_written = true;
+ else if (var == var3)
+ *var3_written = true;
+ }
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Set clip_distance_array_size based and cull_distance_array_size on the given
+ * shader.
+ *
+ * Also check for errors based on incorrect usage of gl_ClipVertex and
+ * gl_ClipDistance and gl_CullDistance.
+ * Additionally test whether the arrays gl_ClipDistance and gl_CullDistance
+ * exceed the maximum size defined by gl_MaxCombinedClipAndCullDistances.
+ */
+static void
+analyze_clip_cull_usage(struct gl_shader_program *prog, nir_shader *shader,
+ const struct gl_constants *consts,
+ struct shader_info *info)
+{
+ if (consts->DoDCEBeforeClipCullAnalysis) {
+ /* Remove dead functions to avoid raising an error (eg: dead function
+ * writes to gl_ClipVertex, and main() writes to gl_ClipDistance).
+ */
+ remove_dead_functions(shader);
+ }
+
+ info->clip_distance_array_size = 0;
+ info->cull_distance_array_size = 0;
+
+ if (prog->GLSL_Version >= (prog->IsES ? 300 : 130)) {
+ /* From section 7.1 (Vertex Shader Special Variables) of the
+ * GLSL 1.30 spec:
+ *
+ * "It is an error for a shader to statically write both
+ * gl_ClipVertex and gl_ClipDistance."
+ *
+ * This does not apply to GLSL ES shaders, since GLSL ES defines neither
+ * gl_ClipVertex nor gl_ClipDistance. However with
+ * GL_EXT_clip_cull_distance, this functionality is exposed in ES 3.0.
+ */
+ nir_variable *clip_dist =
+ nir_find_variable_with_location(shader,
+ nir_var_shader_out,
+ VARYING_SLOT_CLIP_DIST0);
+ nir_variable *cull_dist =
+ nir_find_variable_with_location(shader,
+ nir_var_shader_out,
+ VARYING_SLOT_CULL_DIST0);
+ nir_variable *clip_vert =
+ nir_find_variable_with_location(shader,
+ nir_var_shader_out,
+ VARYING_SLOT_CLIP_VERTEX);
+
+ bool clip_dist_written = false;
+ bool cull_dist_written = false;
+ bool clip_vert_written = false;
+ find_assignments(shader, clip_dist, cull_dist, clip_vert,
+ &clip_dist_written, &cull_dist_written,
+ &clip_vert_written);
+
+ /* From the ARB_cull_distance spec:
+ *
+ * It is a compile-time or link-time error for the set of shaders forming
+ * a program to statically read or write both gl_ClipVertex and either
+ * gl_ClipDistance or gl_CullDistance.
+ *
+ * This does not apply to GLSL ES shaders, since GLSL ES doesn't define
+ * gl_ClipVertex.
+ */
+ if (!prog->IsES) {
+ if (clip_vert_written && clip_dist_written) {
+ linker_error(prog, "%s shader writes to both `gl_ClipVertex' "
+ "and `gl_ClipDistance'\n",
+ _mesa_shader_stage_to_string(info->stage));
+ return;
+ }
+ if (clip_vert_written && cull_dist_written) {
+ linker_error(prog, "%s shader writes to both `gl_ClipVertex' "
+ "and `gl_CullDistance'\n",
+ _mesa_shader_stage_to_string(info->stage));
+ return;
+ }
+ }
+
+ if (clip_dist_written)
+ info->clip_distance_array_size = glsl_get_length(clip_dist->type);
+
+ if (cull_dist_written)
+ info->cull_distance_array_size = glsl_get_length(cull_dist->type);
+
+ /* From the ARB_cull_distance spec:
+ *
+ * It is a compile-time or link-time error for the set of shaders forming
+ * a program to have the sum of the sizes of the gl_ClipDistance and
+ * gl_CullDistance arrays to be larger than
+ * gl_MaxCombinedClipAndCullDistances.
+ */
+ if ((uint32_t)(info->clip_distance_array_size + info->cull_distance_array_size) >
+ consts->MaxClipPlanes) {
+ linker_error(prog, "%s shader: the combined size of "
+ "'gl_ClipDistance' and 'gl_CullDistance' size cannot "
+ "be larger than "
+ "gl_MaxCombinedClipAndCullDistances (%u)",
+ _mesa_shader_stage_to_string(info->stage),
+ consts->MaxClipPlanes);
+ }
+ }
+}
+
+/**
+ * Verify that a vertex shader executable meets all semantic requirements.
+ *
+ * Also sets info.clip_distance_array_size and
+ * info.cull_distance_array_size as a side effect.
+ *
+ * \param shader Vertex shader executable to be verified
+ */
+static void
+validate_vertex_shader_executable(struct gl_shader_program *prog,
+ nir_shader *shader,
+ const struct gl_constants *consts)
+{
+ if (shader == NULL)
+ return;
+
+ /* From the GLSL 1.10 spec, page 48:
+ *
+ * "The variable gl_Position is available only in the vertex
+ * language and is intended for writing the homogeneous vertex
+ * position. All executions of a well-formed vertex shader
+ * executable must write a value into this variable. [...] The
+ * variable gl_Position is available only in the vertex
+ * language and is intended for writing the homogeneous vertex
+ * position. All executions of a well-formed vertex shader
+ * executable must write a value into this variable."
+ *
+ * while in GLSL 1.40 this text is changed to:
+ *
+ * "The variable gl_Position is available only in the vertex
+ * language and is intended for writing the homogeneous vertex
+ * position. It can be written at any time during shader
+ * execution. It may also be read back by a vertex shader
+ * after being written. This value will be used by primitive
+ * assembly, clipping, culling, and other fixed functionality
+ * operations, if present, that operate on primitives after
+ * vertex processing has occurred. Its value is undefined if
+ * the vertex shader executable does not write gl_Position."
+ *
+ * All GLSL ES Versions are similar to GLSL 1.40--failing to write to
+ * gl_Position is not an error.
+ */
+ if (prog->GLSL_Version < (prog->IsES ? 300 : 140)) {
+ nir_variable *gl_position =
+ nir_find_variable_with_location(shader,
+ nir_var_shader_out,
+ VARYING_SLOT_POS);
+
+ bool gl_position_written = false;
+ find_assignments(shader, gl_position, NULL, NULL, &gl_position_written,
+ NULL, NULL);
+ if (!gl_position_written) {
+ if (prog->IsES) {
+ linker_warning(prog,
+ "vertex shader does not write to `gl_Position'. "
+ "Its value is undefined. \n");
+ } else {
+ linker_error(prog,
+ "vertex shader does not write to `gl_Position'. \n");
+ }
+ return;
+ }
+ }
+
+ analyze_clip_cull_usage(prog, shader, consts, &shader->info);
+}
+
+static void
+validate_tess_eval_shader_executable(struct gl_shader_program *prog,
+ nir_shader *shader,
+ const struct gl_constants *consts)
+{
+ if (shader == NULL)
+ return;
+
+ analyze_clip_cull_usage(prog, shader, consts, &shader->info);
+}
+
+/**
+ * Verify that a fragment shader executable meets all semantic requirements
+ *
+ * \param shader Fragment shader executable to be verified
+ */
+static void
+validate_fragment_shader_executable(struct gl_shader_program *prog,
+ nir_shader *shader)
+{
+ if (shader == NULL)
+ return;
+
+ nir_variable *gl_frag_color =
+ nir_find_variable_with_location(shader,
+ nir_var_shader_out,
+ FRAG_RESULT_COLOR);
+ nir_variable *gl_frag_data =
+ nir_find_variable_with_location(shader,
+ nir_var_shader_out,
+ FRAG_RESULT_DATA0);
+
+ bool gl_frag_color_written = false;
+ bool gl_frag_data_written = false;
+ find_assignments(shader, gl_frag_color, gl_frag_data, NULL,
+ &gl_frag_color_written, &gl_frag_data_written, NULL);
+
+ if (gl_frag_color_written && gl_frag_data_written) {
+ linker_error(prog, "fragment shader writes to both "
+ "`gl_FragColor' and `gl_FragData'\n");
+ }
+}
+
+/**
+ * Verify that a geometry shader executable meets all semantic requirements
+ *
+ * Also sets prog->Geom.VerticesIn, and info.clip_distance_array_sizeand
+ * info.cull_distance_array_size as a side effect.
+ *
+ * \param shader Geometry shader executable to be verified
+ */
+static void
+validate_geometry_shader_executable(struct gl_shader_program *prog,
+ nir_shader *shader,
+ const struct gl_constants *consts)
+{
+ if (shader == NULL)
+ return;
+
+ unsigned num_vertices =
+ mesa_vertices_per_prim(shader->info.gs.input_primitive);
+ shader->info.gs.vertices_in = num_vertices;
+
+ analyze_clip_cull_usage(prog, shader, consts, &shader->info);
+}
+
bool
gl_nir_link_glsl(struct gl_context *ctx, struct gl_shader_program *prog)
{
+ const struct gl_constants *consts = &ctx->Const;
+ const struct gl_extensions *exts = &ctx->Extensions;
+ gl_api api = ctx->API;
+
+ if (prog->NumShaders == 0)
+ return true;
+
+ MESA_TRACE_FUNC();
+
+ /* Link all shaders for a particular stage and validate the result.
+ */
+ for (int stage = 0; stage < MESA_SHADER_STAGES; stage++) {
+ struct gl_linked_shader *sh = prog->_LinkedShaders[stage];
+ if (sh) {
+ nir_shader *shader = sh->Program->nir;
+
+ switch (stage) {
+ case MESA_SHADER_VERTEX:
+ validate_vertex_shader_executable(prog, shader, consts);
+ break;
+ case MESA_SHADER_TESS_CTRL:
+ /* nothing to be done */
+ break;
+ case MESA_SHADER_TESS_EVAL:
+ validate_tess_eval_shader_executable(prog, shader, consts);
+ break;
+ case MESA_SHADER_GEOMETRY:
+ validate_geometry_shader_executable(prog, shader, consts);
+ break;
+ case MESA_SHADER_FRAGMENT:
+ validate_fragment_shader_executable(prog, shader);
+ break;
+ }
+ if (!prog->data->LinkStatus) {
+ _mesa_delete_linked_shader(ctx, sh);
+
+ prog->_LinkedShaders[stage] = NULL;
+ prog->data->linked_stages ^= 1 << stage;
+
+ return false;
+ }
+ }
+ }
+
+ /* Here begins the inter-stage linking phase. Some initial validation is
+ * performed, then locations are assigned for uniforms, attributes, and
+ * varyings.
+ */
+ cross_validate_uniforms(consts, prog);
+ if (!prog->data->LinkStatus)
+ return false;
+
+ check_explicit_uniform_locations(exts, prog);
+
+ link_assign_subroutine_types(prog);
+ verify_subroutine_associated_funcs(prog);
+ if (!prog->data->LinkStatus)
+ return false;
+
+ for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
+ if (prog->_LinkedShaders[i] == NULL)
+ continue;
+
+ gl_nir_detect_recursion_linked(prog,
+ prog->_LinkedShaders[i]->Program->nir);
+ if (!prog->data->LinkStatus)
+ return false;
+
+ gl_nir_inline_functions(prog->_LinkedShaders[i]->Program->nir);
+ }
+
+ resize_tes_inputs(consts, prog);
+
+ /* Validate the inputs of each stage with the output of the preceding
+ * stage.
+ */
+ unsigned prev = MESA_SHADER_STAGES;
+ for (unsigned i = 0; i <= MESA_SHADER_FRAGMENT; i++) {
+ if (prog->_LinkedShaders[i] == NULL)
+ continue;
+
+ if (prev == MESA_SHADER_STAGES) {
+ prev = i;
+ continue;
+ }
+
+ gl_nir_validate_interstage_inout_blocks(prog, prog->_LinkedShaders[prev],
+ prog->_LinkedShaders[i]);
+ if (!prog->data->LinkStatus)
+ return false;
+
+ prev = i;
+ }
+
+ /* Cross-validate uniform blocks between shader stages */
+ gl_nir_validate_interstage_uniform_blocks(prog, prog->_LinkedShaders);
+ if (!prog->data->LinkStatus)
+ return false;
+
+ if (prog->IsES && prog->GLSL_Version == 100)
+ if (!validate_invariant_builtins(consts, prog,
+ prog->_LinkedShaders[MESA_SHADER_VERTEX],
+ prog->_LinkedShaders[MESA_SHADER_FRAGMENT]))
+ return false;
+
+ /* Check and validate stream emissions in geometry shaders */
+ validate_geometry_shader_emissions(consts, prog);
+
+ prog->last_vert_prog = NULL;
+ for (int i = MESA_SHADER_GEOMETRY; i >= MESA_SHADER_VERTEX; i--) {
+ if (prog->_LinkedShaders[i] == NULL)
+ continue;
+
+ prog->last_vert_prog = prog->_LinkedShaders[i]->Program;
+ break;
+ }
+
+ unsigned first = MESA_SHADER_STAGES;
+ unsigned last = 0;
+
+ /* Determine first and last stage. */
+ for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
+ if (!prog->_LinkedShaders[i])
+ continue;
+ if (first == MESA_SHADER_STAGES)
+ first = i;
+ last = i;
+ }
+
+ /* Implement the GLSL 1.30+ rule for discard vs infinite loops.
+ * This rule also applies to GLSL ES 3.00.
+ */
+ if (prog->GLSL_Version >= (prog->IsES ? 300 : 130)) {
+ struct gl_linked_shader *sh = prog->_LinkedShaders[MESA_SHADER_FRAGMENT];
+ if (sh)
+ gl_nir_lower_discard_flow(sh->Program->nir);
+ }
+
+ gl_nir_lower_named_interface_blocks(prog);
+
+ /* Validate the inputs of each stage with the output of the preceding
+ * stage.
+ */
+ prev = first;
+ for (unsigned i = prev + 1; i <= MESA_SHADER_FRAGMENT; i++) {
+ if (prog->_LinkedShaders[i] == NULL)
+ continue;
+
+ gl_nir_cross_validate_outputs_to_inputs(consts, prog,
+ prog->_LinkedShaders[prev],
+ prog->_LinkedShaders[i]);
+ if (!prog->data->LinkStatus)
+ return false;
+
+ prev = i;
+ }
+
+ /* The cross validation of outputs/inputs above validates interstage
+ * explicit locations. We need to do this also for the inputs in the first
+ * stage and outputs of the last stage included in the program, since there
+ * is no cross validation for these.
+ */
+ gl_nir_validate_first_and_last_interface_explicit_locations(consts, prog,
+ (gl_shader_stage) first,
+ (gl_shader_stage) last);
+
+ if (prog->SeparateShader)
+ disable_varying_optimizations_for_sso(prog);
+
+ struct gl_linked_shader *linked_shader[MESA_SHADER_STAGES];
+ unsigned num_shaders = 0;
+
+ for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
+ if (prog->_LinkedShaders[i]) {
+ linked_shader[num_shaders++] = prog->_LinkedShaders[i];
+
+ /* Section 13.46 (Vertex Attribute Aliasing) of the OpenGL ES 3.2
+ * specification says:
+ *
+ * "In general, the behavior of GLSL ES should not depend on
+ * compiler optimizations which might be implementation-dependent.
+ * Name matching rules in most languages, including C++ from which
+ * GLSL ES is derived, are based on declarations rather than use.
+ *
+ * RESOLUTION: The existence of aliasing is determined by
+ * declarations present after preprocessing."
+ *
+ * Because of this rule, we don't remove dead attributes before
+ * attribute assignment for vertex shader inputs here.
+ */
+ if (!(prog->IsES && prog->GLSL_Version >= 300 && i == MESA_SHADER_VERTEX))
+ remove_dead_varyings_pre_linking(prog->_LinkedShaders[i]->Program->nir);
+ }
+ }
+
+ if (!gl_assign_attribute_or_color_locations(consts, prog))
+ return false;
+
+ if (!prelink_lowering(consts, exts, prog, linked_shader, num_shaders))
+ return false;
+
+ if (!gl_nir_link_varyings(consts, exts, api, prog))
+ return false;
+
+ /* Validation for special cases where we allow sampler array indexing
+ * with loop induction variable. This check emits a warning or error
+ * depending if backend can handle dynamic indexing.
+ */
+ if ((!prog->IsES && prog->GLSL_Version < 130) ||
+ (prog->IsES && prog->GLSL_Version < 300)) {
+ if (!validate_sampler_array_indexing(consts, prog))
+ return false;
+ }
+
+ if (prog->data->LinkStatus == LINKING_FAILURE)
+ return false;
+
+ if (!linked_shader[0]->Program->nir->info.io_lowered) {
+ /* Linking the stages in the opposite order (from fragment to vertex)
+ * ensures that inter-shader outputs written to in an earlier stage
+ * are eliminated if they are (transitively) not used in a later
+ * stage.
+ */
+ for (int i = num_shaders - 2; i >= 0; i--) {
+ gl_nir_link_opts(linked_shader[i]->Program->nir,
+ linked_shader[i + 1]->Program->nir);
+ }
+ }
+
+ /* Tidy up any left overs from the linking process for single shaders.
+ * For example varying arrays that get packed may have dead elements that
+ * can be now be eliminated now that array access has been lowered.
+ */
+ if (num_shaders == 1)
+ gl_nir_opts(linked_shader[0]->Program->nir);
+
for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
struct gl_linked_shader *shader = prog->_LinkedShaders[i];
if (shader) {
+ if (consts->GLSLLowerConstArrays) {
+ nir_lower_const_arrays_to_uniforms(shader->Program->nir,
+ consts->Program[i].MaxUniformComponents);
+ }
+
const nir_remove_dead_variables_options opts = {
- .can_remove_var = can_remove_uniform,
+ .can_remove_var = can_remove_var,
};
- nir_remove_dead_variables(shader->Program->nir, nir_var_uniform,
+ nir_remove_dead_variables(shader->Program->nir,
+ nir_var_uniform | nir_var_image |
+ nir_var_mem_ubo | nir_var_mem_ssbo |
+ nir_var_system_value,
&opts);
+
+ if (shader->Program->info.stage == MESA_SHADER_FRAGMENT) {
+ nir_shader *nir = shader->Program->nir;
+ nir_foreach_variable_in_shader(var, nir) {
+ if (var->data.mode == nir_var_system_value &&
+ (var->data.location == SYSTEM_VALUE_SAMPLE_ID ||
+ var->data.location == SYSTEM_VALUE_SAMPLE_POS))
+ nir->info.fs.uses_sample_shading = true;
+
+ if (var->data.mode == nir_var_shader_in && var->data.sample)
+ nir->info.fs.uses_sample_shading = true;
+
+ if (var->data.mode == nir_var_shader_out &&
+ var->data.fb_fetch_output)
+ nir->info.fs.uses_sample_shading = true;
+ }
+ }
}
}
- if (!gl_nir_link_uniforms(ctx, prog, true))
+ if (!gl_nir_link_uniform_blocks(consts, prog))
+ return false;
+
+ if (!gl_nir_link_uniforms(consts, prog, true))
return false;
link_util_calculate_subroutine_compat(prog);
- link_util_check_uniform_resources(ctx, prog);
+ link_util_check_uniform_resources(consts, prog);
link_util_check_subroutine_resources(prog);
- check_image_resources(ctx, prog);
- gl_nir_link_assign_atomic_counter_resources(ctx, prog);
- gl_nir_link_check_atomic_counter_resources(ctx, prog);
+ check_image_resources(consts, exts, prog);
+ gl_nir_link_assign_atomic_counter_resources(consts, prog);
+ gl_nir_link_check_atomic_counter_resources(consts, prog);
+
+ /* OpenGL ES < 3.1 requires that a vertex shader and a fragment shader both
+ * be present in a linked program. GL_ARB_ES2_compatibility doesn't say
+ * anything about shader linking when one of the shaders (vertex or
+ * fragment shader) is absent. So, the extension shouldn't change the
+ * behavior specified in GLSL specification.
+ *
+ * From OpenGL ES 3.1 specification (7.3 Program Objects):
+ * "Linking can fail for a variety of reasons as specified in the
+ * OpenGL ES Shading Language Specification, as well as any of the
+ * following reasons:
+ *
+ * ...
+ *
+ * * program contains objects to form either a vertex shader or
+ * fragment shader, and program is not separable, and does not
+ * contain objects to form both a vertex shader and fragment
+ * shader."
+ *
+ * However, the only scenario in 3.1+ where we don't require them both is
+ * when we have a compute shader. For example:
+ *
+ * - No shaders is a link error.
+ * - Geom or Tess without a Vertex shader is a link error which means we
+ * always require a Vertex shader and hence a Fragment shader.
+ * - Finally a Compute shader linked with any other stage is a link error.
+ */
+ if (!prog->SeparateShader && _mesa_is_api_gles2(api) &&
+ !prog->_LinkedShaders[MESA_SHADER_COMPUTE]) {
+ if (prog->_LinkedShaders[MESA_SHADER_VERTEX] == NULL) {
+ linker_error(prog, "program lacks a vertex shader\n");
+ } else if (prog->_LinkedShaders[MESA_SHADER_FRAGMENT] == NULL) {
+ linker_error(prog, "program lacks a fragment shader\n");
+ }
+ }
if (prog->data->LinkStatus == LINKING_FAILURE)
return false;