#include <ctype.h>
#include "util/strndup.h"
#include "glsl_symbol_table.h"
#include "glsl_parser_extras.h"
#include "ir.h"
#include "program.h"
#include "program/prog_instruction.h"
#include "program/program.h"
#include "util/mesa-sha1.h"
#include "util/set.h"
#include "string_to_uint_map.h"
#include "linker.h"
#include "linker_util.h"
#include "link_varyings.h"
#include "ir_optimization.h"
#include "ir_rvalue_visitor.h"
#include "ir_uniform.h"
#include "builtin_functions.h"
#include "shader_cache.h"
#include "util/u_string.h"
#include "util/u_math.h"
#include "main/shaderobj.h"
#include "main/enums.h"
#include "main/mtypes.h"
namespace {
struct find_variable {
const char *name;
bool found;
find_variable(const char *name) : name(name), found(false) {}
};
class find_assignment_visitor : public ir_hierarchical_visitor {
public:
find_assignment_visitor(unsigned num_vars,
find_variable * const *vars)
: num_variables(num_vars), num_found(0), variables(vars)
{
}
virtual ir_visitor_status visit_enter(ir_assignment *ir)
{
ir_variable *const var = ir->lhs->variable_referenced();
return check_variable_name(var->name);
}
virtual ir_visitor_status visit_enter(ir_call *ir)
{
foreach_two_lists(formal_node, &ir->callee->parameters,
actual_node, &ir->actual_parameters) {
ir_rvalue *param_rval = (ir_rvalue *) actual_node;
ir_variable *sig_param = (ir_variable *) formal_node;
if (sig_param->data.mode == ir_var_function_out ||
sig_param->data.mode == ir_var_function_inout) {
ir_variable *var = param_rval->variable_referenced();
if (var && check_variable_name(var->name) == visit_stop)
return visit_stop;
}
}
if (ir->return_deref != NULL) {
ir_variable *const var = ir->return_deref->variable_referenced();
if (check_variable_name(var->name) == visit_stop)
return visit_stop;
}
return visit_continue_with_parent;
}
private:
ir_visitor_status check_variable_name(const char *name)
{
for (unsigned i = 0; i < num_variables; ++i) {
if (strcmp(variables[i]->name, name) == 0) {
if (!variables[i]->found) {
variables[i]->found = true;
assert(num_found < num_variables);
if (++num_found == num_variables)
return visit_stop;
}
break;
}
}
return visit_continue_with_parent;
}
private:
unsigned num_variables;
unsigned num_found;
find_variable * const *variables;
};
static void
find_assignments(exec_list *ir, find_variable * const *vars)
{
unsigned num_variables = 0;
for (find_variable * const *v = vars; *v; ++v)
num_variables++;
find_assignment_visitor visitor(num_variables, vars);
visitor.run(ir);
}
static void
find_assignments(exec_list *ir, find_variable *var)
{
find_assignment_visitor visitor(1, &var);
visitor.run(ir);
}
class find_deref_visitor : public ir_hierarchical_visitor {
public:
find_deref_visitor(const char *name)
: name(name), found(false)
{
}
virtual ir_visitor_status visit(ir_dereference_variable *ir)
{
if (strcmp(this->name, ir->var->name) == 0) {
this->found = true;
return visit_stop;
}
return visit_continue;
}
bool variable_found() const
{
return this->found;
}
private:
const char *name;
bool found;
};
class deref_type_updater : public ir_hierarchical_visitor {
public:
virtual ir_visitor_status visit(ir_dereference_variable *ir)
{
ir->type = ir->var->type;
return visit_continue;
}
virtual ir_visitor_status visit_leave(ir_dereference_array *ir)
{
const glsl_type *const vt = ir->array->type;
if (vt->is_array())
ir->type = vt->fields.array;
return visit_continue;
}
virtual ir_visitor_status visit_leave(ir_dereference_record *ir)
{
ir->type = ir->record->type->fields.structure[ir->field_idx].type;
return visit_continue;
}
};
class array_resize_visitor : public deref_type_updater {
public:
using deref_type_updater::visit;
unsigned num_vertices;
gl_shader_program *prog;
gl_shader_stage stage;
array_resize_visitor(unsigned num_vertices,
gl_shader_program *prog,
gl_shader_stage stage)
{
this->num_vertices = num_vertices;
this->prog = prog;
this->stage = stage;
}
virtual ~array_resize_visitor()
{
}
virtual ir_visitor_status visit(ir_variable *var)
{
if (!var->type->is_array() || var->data.mode != ir_var_shader_in ||
var->data.patch)
return visit_continue;
unsigned size = var->type->length;
if (stage == MESA_SHADER_GEOMETRY) {
if (!var->data.implicit_sized_array &&
size && size != this->num_vertices) {
linker_error(this->prog, "size of array %s declared as %u, "
"but number of input vertices is %u\n",
var->name, size, this->num_vertices);
return visit_continue;
}
if (var->data.max_array_access >= (int)this->num_vertices) {
linker_error(this->prog, "%s shader accesses element %i of "
"%s, but only %i input vertices\n",
_mesa_shader_stage_to_string(this->stage),
var->data.max_array_access, var->name, this->num_vertices);
return visit_continue;
}
}
var->type = glsl_type::get_array_instance(var->type->fields.array,
this->num_vertices);
var->data.max_array_access = this->num_vertices - 1;
return visit_continue;
}
};
class find_emit_vertex_visitor : public ir_hierarchical_visitor {
public:
find_emit_vertex_visitor(int max_allowed)
: max_stream_allowed(max_allowed),
invalid_stream_id(0),
invalid_stream_id_from_emit_vertex(false),
end_primitive_found(false),
uses_non_zero_stream(false)
{
}
virtual ir_visitor_status visit_leave(ir_emit_vertex *ir)
{
int stream_id = ir->stream_id();
if (stream_id < 0) {
invalid_stream_id = stream_id;
invalid_stream_id_from_emit_vertex = true;
return visit_stop;
}
if (stream_id > max_stream_allowed) {
invalid_stream_id = stream_id;
invalid_stream_id_from_emit_vertex = true;
return visit_stop;
}
if (stream_id != 0)
uses_non_zero_stream = true;
return visit_continue;
}
virtual ir_visitor_status visit_leave(ir_end_primitive *ir)
{
end_primitive_found = true;
int stream_id = ir->stream_id();
if (stream_id < 0) {
invalid_stream_id = stream_id;
invalid_stream_id_from_emit_vertex = false;
return visit_stop;
}
if (stream_id > max_stream_allowed) {
invalid_stream_id = stream_id;
invalid_stream_id_from_emit_vertex = false;
return visit_stop;
}
if (stream_id != 0)
uses_non_zero_stream = true;
return visit_continue;
}
bool error()
{
return invalid_stream_id != 0;
}
const char *error_func()
{
return invalid_stream_id_from_emit_vertex ?
"EmitStreamVertex" : "EndStreamPrimitive";
}
int error_stream()
{
return invalid_stream_id;
}
bool uses_streams()
{
return uses_non_zero_stream;
}
bool uses_end_primitive()
{
return end_primitive_found;
}
private:
int max_stream_allowed;
int invalid_stream_id;
bool invalid_stream_id_from_emit_vertex;
bool end_primitive_found;
bool uses_non_zero_stream;
};
class dynamic_sampler_array_indexing_visitor : public ir_hierarchical_visitor
{
public:
dynamic_sampler_array_indexing_visitor() :
dynamic_sampler_array_indexing(false)
{
}
ir_visitor_status visit_enter(ir_dereference_array *ir)
{
if (!ir->variable_referenced())
return visit_continue;
if (!ir->variable_referenced()->type->contains_sampler())
return visit_continue;
if (!ir->array_index->constant_expression_value(ralloc_parent(ir))) {
dynamic_sampler_array_indexing = true;
return visit_stop;
}
return visit_continue;
}
bool uses_dynamic_sampler_array_indexing()
{
return dynamic_sampler_array_indexing;
}
private:
bool dynamic_sampler_array_indexing;
};
}
void
linker_error(gl_shader_program *prog, const char *fmt, ...)
{
va_list ap;
ralloc_strcat(&prog->data->InfoLog, "error: ");
va_start(ap, fmt);
ralloc_vasprintf_append(&prog->data->InfoLog, fmt, ap);
va_end(ap);
prog->data->LinkStatus = LINKING_FAILURE;
}
void
linker_warning(gl_shader_program *prog, const char *fmt, ...)
{
va_list ap;
ralloc_strcat(&prog->data->InfoLog, "warning: ");
va_start(ap, fmt);
ralloc_vasprintf_append(&prog->data->InfoLog, fmt, ap);
va_end(ap);
}
long
parse_program_resource_name(const GLchar *name,
const GLchar **out_base_name_end)
{
const size_t len = strlen(name);
*out_base_name_end = name + len;
if (len == 0 || name[len-1] != ']')
return -1;
unsigned i;
for (i = len - 1; (i > 0) && isdigit(name[i-1]); --i)
;
if ((i == 0) || name[i-1] != '[')
return -1;
long array_index = strtol(&name[i], NULL, 10);
if (array_index < 0)
return -1;
if (name[i] == '0' && name[i+1] != ']')
return -1;
*out_base_name_end = name + (i - 1);
return array_index;
}
void
link_invalidate_variable_locations(exec_list *ir)
{
foreach_in_list(ir_instruction, node, ir) {
ir_variable *const var = node->as_variable();
if (var == NULL)
continue;
if (!var->data.explicit_location) {
var->data.location = -1;
var->data.location_frac = 0;
}
if (var->data.explicit_location &&
var->data.location < VARYING_SLOT_VAR0) {
var->data.is_unmatched_generic_inout = 0;
} else {
var->data.is_unmatched_generic_inout = 1;
}
}
}
static void
analyze_clip_cull_usage(struct gl_shader_program *prog,
struct gl_linked_shader *shader,
struct gl_context *ctx,
struct shader_info *info)
{
info->clip_distance_array_size = 0;
info->cull_distance_array_size = 0;
if (prog->data->Version >= (prog->IsES ? 300 : 130)) {
find_variable gl_ClipDistance("gl_ClipDistance");
find_variable gl_CullDistance("gl_CullDistance");
find_variable gl_ClipVertex("gl_ClipVertex");
find_variable * const variables[] = {
&gl_ClipDistance,
&gl_CullDistance,
!prog->IsES ? &gl_ClipVertex : NULL,
NULL
};
find_assignments(shader->ir, variables);
if (!prog->IsES) {
if (gl_ClipVertex.found && gl_ClipDistance.found) {
linker_error(prog, "%s shader writes to both `gl_ClipVertex' "
"and `gl_ClipDistance'\n",
_mesa_shader_stage_to_string(shader->Stage));
return;
}
if (gl_ClipVertex.found && gl_CullDistance.found) {
linker_error(prog, "%s shader writes to both `gl_ClipVertex' "
"and `gl_CullDistance'\n",
_mesa_shader_stage_to_string(shader->Stage));
return;
}
}
if (gl_ClipDistance.found) {
ir_variable *clip_distance_var =
shader->symbols->get_variable("gl_ClipDistance");
assert(clip_distance_var);
info->clip_distance_array_size = clip_distance_var->type->length;
}
if (gl_CullDistance.found) {
ir_variable *cull_distance_var =
shader->symbols->get_variable("gl_CullDistance");
assert(cull_distance_var);
info->cull_distance_array_size = cull_distance_var->type->length;
}
if ((uint32_t)(info->clip_distance_array_size + info->cull_distance_array_size) >
ctx->Const.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(shader->Stage),
ctx->Const.MaxClipPlanes);
}
}
}
static void
validate_vertex_shader_executable(struct gl_shader_program *prog,
struct gl_linked_shader *shader,
struct gl_context *ctx)
{
if (shader == NULL)
return;
if (prog->data->Version < (prog->IsES ? 300 : 140)) {
find_variable gl_Position("gl_Position");
find_assignments(shader->ir, &gl_Position);
if (!gl_Position.found) {
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, ctx, &shader->Program->info);
}
static void
validate_tess_eval_shader_executable(struct gl_shader_program *prog,
struct gl_linked_shader *shader,
struct gl_context *ctx)
{
if (shader == NULL)
return;
analyze_clip_cull_usage(prog, shader, ctx, &shader->Program->info);
}
static void
validate_fragment_shader_executable(struct gl_shader_program *prog,
struct gl_linked_shader *shader)
{
if (shader == NULL)
return;
find_variable gl_FragColor("gl_FragColor");
find_variable gl_FragData("gl_FragData");
find_variable * const variables[] = { &gl_FragColor, &gl_FragData, NULL };
find_assignments(shader->ir, variables);
if (gl_FragColor.found && gl_FragData.found) {
linker_error(prog, "fragment shader writes to both "
"`gl_FragColor' and `gl_FragData'\n");
}
}
static void
validate_geometry_shader_executable(struct gl_shader_program *prog,
struct gl_linked_shader *shader,
struct gl_context *ctx)
{
if (shader == NULL)
return;
unsigned num_vertices =
vertices_per_prim(shader->Program->info.gs.input_primitive);
prog->Geom.VerticesIn = num_vertices;
analyze_clip_cull_usage(prog, shader, ctx, &shader->Program->info);
}
static void
validate_geometry_shader_emissions(struct gl_context *ctx,
struct gl_shader_program *prog)
{
struct gl_linked_shader *sh = prog->_LinkedShaders[MESA_SHADER_GEOMETRY];
if (sh != NULL) {
find_emit_vertex_visitor emit_vertex(ctx->Const.MaxVertexStreams - 1);
emit_vertex.run(sh->ir);
if (emit_vertex.error()) {
linker_error(prog, "Invalid call %s(%d). Accepted values for the "
"stream parameter are in the range [0, %d].\n",
emit_vertex.error_func(),
emit_vertex.error_stream(),
ctx->Const.MaxVertexStreams - 1);
}
prog->Geom.UsesStreams = emit_vertex.uses_streams();
prog->Geom.UsesEndPrimitive = emit_vertex.uses_end_primitive();
if (prog->Geom.UsesStreams &&
sh->Program->info.gs.output_primitive != GL_POINTS) {
linker_error(prog, "EmitStreamVertex(n) and EndStreamPrimitive(n) "
"with n>0 requires point output\n");
}
}
}
bool
validate_intrastage_arrays(struct gl_shader_program *prog,
ir_variable *const var,
ir_variable *const existing,
bool match_precision)
{
if (var->type->is_array() && existing->type->is_array()) {
const glsl_type *no_array_var = var->type->fields.array;
const glsl_type *no_array_existing = existing->type->fields.array;
bool type_matches;
type_matches = (match_precision ?
no_array_var == no_array_existing :
no_array_var->compare_no_precision(no_array_existing));
if (type_matches &&
((var->type->length == 0)|| (existing->type->length == 0))) {
if (var->type->length != 0) {
if ((int)var->type->length <= existing->data.max_array_access) {
linker_error(prog, "%s `%s' declared as type "
"`%s' but outermost dimension has an index"
" of `%i'\n",
mode_string(var),
var->name, var->type->name,
existing->data.max_array_access);
}
existing->type = var->type;
return true;
} else if (existing->type->length != 0) {
if((int)existing->type->length <= 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",
mode_string(var),
var->name, existing->type->name,
var->data.max_array_access);
}
return true;
}
}
}
return false;
}
static void
cross_validate_globals(struct gl_context *ctx, struct gl_shader_program *prog,
struct exec_list *ir, glsl_symbol_table *variables,
bool uniforms_only)
{
foreach_in_list(ir_instruction, node, ir) {
ir_variable *const var = node->as_variable();
if (var == NULL)
continue;
if (uniforms_only && (var->data.mode != ir_var_uniform && var->data.mode != ir_var_shader_storage))
continue;
if (var->type->contains_subroutine())
continue;
if (var->is_interface_instance())
continue;
if (var->data.mode == ir_var_temporary)
continue;
ir_variable *const existing = variables->get_variable(var->name);
if (existing != NULL) {
if (var->type != existing->type) {
if (!validate_intrastage_arrays(prog, var, existing)) {
if (!(var->data.mode == ir_var_shader_storage &&
var->data.from_ssbo_unsized_array &&
existing->data.mode == ir_var_shader_storage &&
existing->data.from_ssbo_unsized_array &&
var->type->gl_type == existing->type->gl_type)) {
linker_error(prog, "%s `%s' declared as type "
"`%s' and type `%s'\n",
mode_string(var),
var->name, var->type->name,
existing->type->name);
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",
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", mode_string(var), var->name);
return;
}
existing->data.location = var->data.location;
existing->data.explicit_location = true;
} else {
if (existing->data.explicit_location) {
var->data.location = existing->data.location;
var->data.explicit_location = true;
}
}
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",
mode_string(var), var->name);
return;
}
existing->data.binding = var->data.binding;
existing->data.explicit_binding = true;
}
if (var->type->contains_atomic() &&
var->data.offset != existing->data.offset) {
linker_error(prog, "offset specifications for %s "
"`%s' have differing values\n",
mode_string(var), var->name);
return;
}
if (strcmp(var->name, "gl_FragDepth") == 0) {
bool layout_declared = var->data.depth_layout != ir_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");
}
}
if (var->constant_initializer != NULL) {
if (existing->constant_initializer != NULL) {
if (!var->constant_initializer->has_value(existing->constant_initializer)) {
linker_error(prog, "initializers for %s "
"`%s' have differing values\n",
mode_string(var), var->name);
return;
}
} else {
variables->replace_variable(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",
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",
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",
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",
mode_string(var), var->name);
return;
}
if (!ctx->Const.AllowGLSLRelaxedES &&
prog->IsES && !var->get_interface_type() &&
existing->data.precision != var->data.precision) {
if ((existing->data.used && var->data.used) || prog->data->Version >= 300) {
linker_error(prog, "declarations for %s `%s` have "
"mismatching precision qualifiers\n",
mode_string(var), var->name);
return;
} else {
linker_warning(prog, "declarations for %s `%s` have "
"mismatching precision qualifiers\n",
mode_string(var), var->name);
}
}
const glsl_type *var_itype = var->get_interface_type();
const glsl_type *existing_itype = existing->get_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",
mode_string(var), var->name,
var_itype ? var_itype->name : existing_itype->name);
return;
} else if (strcmp(var_itype->name, existing_itype->name) != 0) {
linker_error(prog, "declarations for %s `%s` are inside blocks "
"`%s` and `%s`",
mode_string(var), var->name,
existing_itype->name,
var_itype->name);
return;
}
}
} else
variables->add_variable(var);
}
}
static void
cross_validate_uniforms(struct gl_context *ctx,
struct gl_shader_program *prog)
{
glsl_symbol_table variables;
for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
if (prog->_LinkedShaders[i] == NULL)
continue;
cross_validate_globals(ctx, prog, prog->_LinkedShaders[i]->ir,
&variables, true);
}
}
static bool
interstage_cross_validate_uniform_blocks(struct gl_shader_program *prog,
bool validate_ssbo)
{
int *InterfaceBlockStageIndex[MESA_SHADER_STAGES];
struct gl_uniform_block *blks = NULL;
unsigned *num_blks = validate_ssbo ? &prog->data->NumShaderStorageBlocks :
&prog->data->NumUniformBlocks;
unsigned max_num_buffer_blocks = 0;
for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
if (prog->_LinkedShaders[i]) {
if (validate_ssbo) {
max_num_buffer_blocks +=
prog->_LinkedShaders[i]->Program->info.num_ssbos;
} else {
max_num_buffer_blocks +=
prog->_LinkedShaders[i]->Program->info.num_ubos;
}
}
}
for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
struct gl_linked_shader *sh = prog->_LinkedShaders[i];
InterfaceBlockStageIndex[i] = new int[max_num_buffer_blocks];
for (unsigned int j = 0; j < max_num_buffer_blocks; j++)
InterfaceBlockStageIndex[i][j] = -1;
if (sh == NULL)
continue;
unsigned sh_num_blocks;
struct gl_uniform_block **sh_blks;
if (validate_ssbo) {
sh_num_blocks = prog->_LinkedShaders[i]->Program->info.num_ssbos;
sh_blks = sh->Program->sh.ShaderStorageBlocks;
} else {
sh_num_blocks = prog->_LinkedShaders[i]->Program->info.num_ubos;
sh_blks = sh->Program->sh.UniformBlocks;
}
for (unsigned int j = 0; j < sh_num_blocks; j++) {
int index = link_cross_validate_uniform_block(prog->data, &blks,
num_blks, sh_blks[j]);
if (index == -1) {
linker_error(prog, "buffer block `%s' has mismatching "
"definitions\n", sh_blks[j]->Name);
for (unsigned k = 0; k <= i; k++) {
delete[] InterfaceBlockStageIndex[k];
}
*num_blks = 0;
return false;
}
InterfaceBlockStageIndex[i][index] = j;
}
}
for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
for (unsigned j = 0; j < *num_blks; j++) {
int stage_index = InterfaceBlockStageIndex[i][j];
if (stage_index != -1) {
struct gl_linked_shader *sh = prog->_LinkedShaders[i];
struct gl_uniform_block **sh_blks = validate_ssbo ?
sh->Program->sh.ShaderStorageBlocks :
sh->Program->sh.UniformBlocks;
blks[j].stageref |= sh_blks[stage_index]->stageref;
sh_blks[stage_index] = &blks[j];
}
}
}
for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
delete[] InterfaceBlockStageIndex[i];
}
if (validate_ssbo)
prog->data->ShaderStorageBlocks = blks;
else
prog->data->UniformBlocks = blks;
return true;
}
static bool
validate_invariant_builtins(struct gl_shader_program *prog,
const gl_linked_shader *vert,
const gl_linked_shader *frag)
{
const ir_variable *var_vert;
const ir_variable *var_frag;
if (!vert || !frag)
return true;
var_frag = frag->symbols->get_variable("gl_FragCoord");
if (var_frag && var_frag->data.invariant) {
var_vert = vert->symbols->get_variable("gl_Position");
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 = frag->symbols->get_variable("gl_PointCoord");
if (var_frag && var_frag->data.invariant) {
var_vert = vert->symbols->get_variable("gl_PointSize");
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 = frag->symbols->get_variable("gl_FrontFacing");
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
populate_symbol_table(gl_linked_shader *sh, glsl_symbol_table *symbols)
{
sh->symbols = new(sh) glsl_symbol_table;
_mesa_glsl_copy_symbols_from_table(sh->ir, symbols, sh->symbols);
}
static void
remap_variables(ir_instruction *inst, struct gl_linked_shader *target,
hash_table *temps)
{
class remap_visitor : public ir_hierarchical_visitor {
public:
remap_visitor(struct gl_linked_shader *target, hash_table *temps)
{
this->target = target;
this->symbols = target->symbols;
this->instructions = target->ir;
this->temps = temps;
}
virtual ir_visitor_status visit(ir_dereference_variable *ir)
{
if (ir->var->data.mode == ir_var_temporary) {
hash_entry *entry = _mesa_hash_table_search(temps, ir->var);
ir_variable *var = entry ? (ir_variable *) entry->data : NULL;
assert(var != NULL);
ir->var = var;
return visit_continue;
}
ir_variable *const existing =
this->symbols->get_variable(ir->var->name);
if (existing != NULL)
ir->var = existing;
else {
ir_variable *copy = ir->var->clone(this->target, NULL);
this->symbols->add_variable(copy);
this->instructions->push_head(copy);
ir->var = copy;
}
return visit_continue;
}
private:
struct gl_linked_shader *target;
glsl_symbol_table *symbols;
exec_list *instructions;
hash_table *temps;
};
remap_visitor v(target, temps);
inst->accept(&v);
}
static exec_node *
move_non_declarations(exec_list *instructions, exec_node *last,
bool make_copies, gl_linked_shader *target)
{
hash_table *temps = NULL;
if (make_copies)
temps = _mesa_pointer_hash_table_create(NULL);
foreach_in_list_safe(ir_instruction, inst, instructions) {
if (inst->as_function())
continue;
if (inst->ir_type == ir_type_precision)
continue;
if (inst->ir_type == ir_type_typedecl)
continue;
ir_variable *var = inst->as_variable();
if ((var != NULL) && (var->data.mode != ir_var_temporary))
continue;
assert(inst->as_assignment()
|| inst->as_call()
|| inst->as_if()
|| ((var != NULL) && (var->data.mode == ir_var_temporary)));
if (make_copies) {
inst = inst->clone(target, NULL);
if (var != NULL)
_mesa_hash_table_insert(temps, var, inst);
else
remap_variables(inst, target, temps);
} else {
inst->remove();
}
last->insert_after(inst);
last = inst;
}
if (make_copies)
_mesa_hash_table_destroy(temps, NULL);
return last;
}
class array_sizing_visitor : public deref_type_updater {
public:
using deref_type_updater::visit;
array_sizing_visitor()
: mem_ctx(ralloc_context(NULL)),
unnamed_interfaces(_mesa_pointer_hash_table_create(NULL))
{
}
~array_sizing_visitor()
{
_mesa_hash_table_destroy(this->unnamed_interfaces, NULL);
ralloc_free(this->mem_ctx);
}
virtual ir_visitor_status visit(ir_variable *var)
{
const glsl_type *type_without_array;
bool implicit_sized_array = var->data.implicit_sized_array;
fixup_type(&var->type, var->data.max_array_access,
var->data.from_ssbo_unsized_array,
&implicit_sized_array);
var->data.implicit_sized_array = implicit_sized_array;
type_without_array = var->type->without_array();
if (var->type->is_interface()) {
if (interface_contains_unsized_arrays(var->type)) {
const glsl_type *new_type =
resize_interface_members(var->type,
var->get_max_ifc_array_access(),
var->is_in_shader_storage_block());
var->type = new_type;
var->change_interface_type(new_type);
}
} else if (type_without_array->is_interface()) {
if (interface_contains_unsized_arrays(type_without_array)) {
const glsl_type *new_type =
resize_interface_members(type_without_array,
var->get_max_ifc_array_access(),
var->is_in_shader_storage_block());
var->change_interface_type(new_type);
var->type = update_interface_members_array(var->type, new_type);
}
} else if (const glsl_type *ifc_type = var->get_interface_type()) {
hash_entry *entry =
_mesa_hash_table_search(this->unnamed_interfaces,
ifc_type);
ir_variable **interface_vars = entry ? (ir_variable **) entry->data : NULL;
if (interface_vars == NULL) {
interface_vars = rzalloc_array(mem_ctx, ir_variable *,
ifc_type->length);
_mesa_hash_table_insert(this->unnamed_interfaces, ifc_type,
interface_vars);
}
unsigned index = ifc_type->field_index(var->name);
assert(index < ifc_type->length);
assert(interface_vars[index] == NULL);
interface_vars[index] = var;
}
return visit_continue;
}
void fixup_unnamed_interface_types()
{
hash_table_call_foreach(this->unnamed_interfaces,
fixup_unnamed_interface_type, NULL);
}
private:
static void fixup_type(const glsl_type **type, unsigned max_array_access,
bool from_ssbo_unsized_array, bool *implicit_sized)
{
if (!from_ssbo_unsized_array && (*type)->is_unsized_array()) {
*type = glsl_type::get_array_instance((*type)->fields.array,
max_array_access + 1);
*implicit_sized = true;
assert(*type != NULL);
}
}
static const glsl_type *
update_interface_members_array(const glsl_type *type,
const glsl_type *new_interface_type)
{
const glsl_type *element_type = type->fields.array;
if (element_type->is_array()) {
const glsl_type *new_array_type =
update_interface_members_array(element_type, new_interface_type);
return glsl_type::get_array_instance(new_array_type, type->length);
} else {
return glsl_type::get_array_instance(new_interface_type,
type->length);
}
}
static bool interface_contains_unsized_arrays(const glsl_type *type)
{
for (unsigned i = 0; i < type->length; i++) {
const glsl_type *elem_type = type->fields.structure[i].type;
if (elem_type->is_unsized_array())
return true;
}
return false;
}
static const glsl_type *
resize_interface_members(const glsl_type *type,
const int *max_ifc_array_access,
bool is_ssbo)
{
unsigned num_fields = type->length;
glsl_struct_field *fields = new glsl_struct_field[num_fields];
memcpy(fields, type->fields.structure,
num_fields * sizeof(*fields));
for (unsigned i = 0; i < num_fields; i++) {
bool implicit_sized_array = fields[i].implicit_sized_array;
if (is_ssbo && i == (num_fields - 1))
fixup_type(&fields[i].type, max_ifc_array_access[i],
true, &implicit_sized_array);
else
fixup_type(&fields[i].type, max_ifc_array_access[i],
false, &implicit_sized_array);
fields[i].implicit_sized_array = implicit_sized_array;
}
glsl_interface_packing packing =
(glsl_interface_packing) type->interface_packing;
bool row_major = (bool) type->interface_row_major;
const glsl_type *new_ifc_type =
glsl_type::get_interface_instance(fields, num_fields,
packing, row_major, type->name);
delete [] fields;
return new_ifc_type;
}
static void fixup_unnamed_interface_type(const void *key, void *data,
void *)
{
const glsl_type *ifc_type = (const glsl_type *) key;
ir_variable **interface_vars = (ir_variable **) data;
unsigned num_fields = ifc_type->length;
glsl_struct_field *fields = new glsl_struct_field[num_fields];
memcpy(fields, ifc_type->fields.structure,
num_fields * sizeof(*fields));
bool interface_type_changed = false;
for (unsigned i = 0; i < num_fields; i++) {
if (interface_vars[i] != NULL &&
fields[i].type != interface_vars[i]->type) {
fields[i].type = interface_vars[i]->type;
interface_type_changed = true;
}
}
if (!interface_type_changed) {
delete [] fields;
return;
}
glsl_interface_packing packing =
(glsl_interface_packing) ifc_type->interface_packing;
bool row_major = (bool) ifc_type->interface_row_major;
const glsl_type *new_ifc_type =
glsl_type::get_interface_instance(fields, num_fields, packing,
row_major, ifc_type->name);
delete [] fields;
for (unsigned i = 0; i < num_fields; i++) {
if (interface_vars[i] != NULL)
interface_vars[i]->change_interface_type(new_ifc_type);
}
}
void *mem_ctx;
hash_table *unnamed_interfaces;
};
static bool
validate_xfb_buffer_stride(struct gl_context *ctx, unsigned idx,
struct gl_shader_program *prog)
{
if (prog->TransformFeedback.BufferStride[idx] % 4) {
linker_error(prog, "invalid qualifier xfb_stride=%d must be a "
"multiple of 4 or if its applied to a type that is "
"or contains a double a multiple of 8.",
prog->TransformFeedback.BufferStride[idx]);
return false;
}
if (prog->TransformFeedback.BufferStride[idx] / 4 >
ctx->Const.MaxTransformFeedbackInterleavedComponents) {
linker_error(prog, "The MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS "
"limit has been exceeded.");
return false;
}
return true;
}
static void
link_xfb_stride_layout_qualifiers(struct gl_context *ctx,
struct gl_shader_program *prog,
struct gl_shader **shader_list,
unsigned num_shaders)
{
for (unsigned i = 0; i < MAX_FEEDBACK_BUFFERS; i++) {
prog->TransformFeedback.BufferStride[i] = 0;
}
for (unsigned i = 0; i < num_shaders; i++) {
struct gl_shader *shader = shader_list[i];
for (unsigned j = 0; j < MAX_FEEDBACK_BUFFERS; j++) {
if (shader->TransformFeedbackBufferStride[j]) {
if (prog->TransformFeedback.BufferStride[j] == 0) {
prog->TransformFeedback.BufferStride[j] =
shader->TransformFeedbackBufferStride[j];
if (!validate_xfb_buffer_stride(ctx, j, prog))
return;
} else if (prog->TransformFeedback.BufferStride[j] !=
shader->TransformFeedbackBufferStride[j]){
linker_error(prog,
"intrastage shaders defined with conflicting "
"xfb_stride for buffer %d (%d and %d)\n", j,
prog->TransformFeedback.BufferStride[j],
shader->TransformFeedbackBufferStride[j]);
return;
}
}
}
}
}
static void
link_bindless_layout_qualifiers(struct gl_shader_program *prog,
struct gl_shader **shader_list,
unsigned num_shaders)
{
bool bindless_sampler, bindless_image;
bool bound_sampler, bound_image;
bindless_sampler = bindless_image = false;
bound_sampler = bound_image = false;
for (unsigned i = 0; i < num_shaders; i++) {
struct gl_shader *shader = shader_list[i];
if (shader->bindless_sampler)
bindless_sampler = true;
if (shader->bindless_image)
bindless_image = true;
if (shader->bound_sampler)
bound_sampler = true;
if (shader->bound_image)
bound_image = true;
if ((bindless_sampler && bound_sampler) ||
(bindless_image && bound_image)) {
linker_error(prog, "both bindless_sampler and bound_sampler, or "
"bindless_image and bound_image, can't be declared at "
"global scope");
}
}
}
static void
link_layer_viewport_relative_qualifier(struct gl_shader_program *prog,
struct gl_program *gl_prog,
struct gl_shader **shader_list,
unsigned num_shaders)
{
unsigned i;
for (i = 0; i < num_shaders; i++) {
if (shader_list[i]->redeclares_gl_layer) {
gl_prog->info.layer_viewport_relative =
shader_list[i]->layer_viewport_relative;
break;
}
}
for (; i < num_shaders; i++) {
if (shader_list[i]->redeclares_gl_layer &&
shader_list[i]->layer_viewport_relative !=
gl_prog->info.layer_viewport_relative) {
linker_error(prog, "all gl_Layer redeclarations must have identical "
"viewport_relative settings");
}
}
}
static void
link_tcs_out_layout_qualifiers(struct gl_shader_program *prog,
struct gl_program *gl_prog,
struct gl_shader **shader_list,
unsigned num_shaders)
{
if (gl_prog->info.stage != MESA_SHADER_TESS_CTRL)
return;
gl_prog->info.tess.tcs_vertices_out = 0;
for (unsigned i = 0; i < num_shaders; i++) {
struct gl_shader *shader = shader_list[i];
if (shader->info.TessCtrl.VerticesOut != 0) {
if (gl_prog->info.tess.tcs_vertices_out != 0 &&
gl_prog->info.tess.tcs_vertices_out !=
(unsigned) shader->info.TessCtrl.VerticesOut) {
linker_error(prog, "tessellation control shader defined with "
"conflicting output vertex count (%d and %d)\n",
gl_prog->info.tess.tcs_vertices_out,
shader->info.TessCtrl.VerticesOut);
return;
}
gl_prog->info.tess.tcs_vertices_out =
shader->info.TessCtrl.VerticesOut;
}
}
if (gl_prog->info.tess.tcs_vertices_out == 0) {
linker_error(prog, "tessellation control shader didn't declare "
"vertices out layout qualifier\n");
return;
}
}
static void
link_tes_in_layout_qualifiers(struct gl_shader_program *prog,
struct gl_program *gl_prog,
struct gl_shader **shader_list,
unsigned num_shaders)
{
if (gl_prog->info.stage != MESA_SHADER_TESS_EVAL)
return;
int point_mode = -1;
unsigned vertex_order = 0;
gl_prog->info.tess.primitive_mode = PRIM_UNKNOWN;
gl_prog->info.tess.spacing = TESS_SPACING_UNSPECIFIED;
for (unsigned i = 0; i < num_shaders; i++) {
struct gl_shader *shader = shader_list[i];
if (shader->info.TessEval.PrimitiveMode != PRIM_UNKNOWN) {
if (gl_prog->info.tess.primitive_mode != PRIM_UNKNOWN &&
gl_prog->info.tess.primitive_mode !=
shader->info.TessEval.PrimitiveMode) {
linker_error(prog, "tessellation evaluation shader defined with "
"conflicting input primitive modes.\n");
return;
}
gl_prog->info.tess.primitive_mode =
shader->info.TessEval.PrimitiveMode;
}
if (shader->info.TessEval.Spacing != 0) {
if (gl_prog->info.tess.spacing != 0 && gl_prog->info.tess.spacing !=
shader->info.TessEval.Spacing) {
linker_error(prog, "tessellation evaluation shader defined with "
"conflicting vertex spacing.\n");
return;
}
gl_prog->info.tess.spacing = shader->info.TessEval.Spacing;
}
if (shader->info.TessEval.VertexOrder != 0) {
if (vertex_order != 0 &&
vertex_order != shader->info.TessEval.VertexOrder) {
linker_error(prog, "tessellation evaluation shader defined with "
"conflicting ordering.\n");
return;
}
vertex_order = shader->info.TessEval.VertexOrder;
}
if (shader->info.TessEval.PointMode != -1) {
if (point_mode != -1 &&
point_mode != shader->info.TessEval.PointMode) {
linker_error(prog, "tessellation evaluation shader defined with "
"conflicting point modes.\n");
return;
}
point_mode = shader->info.TessEval.PointMode;
}
}
if (gl_prog->info.tess.primitive_mode == PRIM_UNKNOWN) {
linker_error(prog,
"tessellation evaluation shader didn't declare input "
"primitive modes.\n");
return;
}
if (gl_prog->info.tess.spacing == TESS_SPACING_UNSPECIFIED)
gl_prog->info.tess.spacing = TESS_SPACING_EQUAL;
if (vertex_order == 0 || vertex_order == GL_CCW)
gl_prog->info.tess.ccw = true;
else
gl_prog->info.tess.ccw = false;
if (point_mode == -1 || point_mode == GL_FALSE)
gl_prog->info.tess.point_mode = false;
else
gl_prog->info.tess.point_mode = true;
}
static void
link_fs_inout_layout_qualifiers(struct gl_shader_program *prog,
struct gl_linked_shader *linked_shader,
struct gl_shader **shader_list,
unsigned num_shaders)
{
bool redeclares_gl_fragcoord = false;
bool uses_gl_fragcoord = false;
bool origin_upper_left = false;
bool pixel_center_integer = false;
if (linked_shader->Stage != MESA_SHADER_FRAGMENT ||
(prog->data->Version < 150 &&
!prog->ARB_fragment_coord_conventions_enable))
return;
for (unsigned i = 0; i < num_shaders; i++) {
struct gl_shader *shader = shader_list[i];
if ((redeclares_gl_fragcoord && !shader->redeclares_gl_fragcoord &&
shader->uses_gl_fragcoord)
|| (shader->redeclares_gl_fragcoord && !redeclares_gl_fragcoord &&
uses_gl_fragcoord)) {
linker_error(prog, "fragment shader defined with conflicting "
"layout qualifiers for gl_FragCoord\n");
}
if (redeclares_gl_fragcoord && shader->redeclares_gl_fragcoord &&
(shader->origin_upper_left != origin_upper_left ||
shader->pixel_center_integer != pixel_center_integer)) {
linker_error(prog, "fragment shader defined with conflicting "
"layout qualifiers for gl_FragCoord\n");
}
if (shader->redeclares_gl_fragcoord || shader->uses_gl_fragcoord) {
redeclares_gl_fragcoord = shader->redeclares_gl_fragcoord;
uses_gl_fragcoord |= shader->uses_gl_fragcoord;
origin_upper_left = shader->origin_upper_left;
pixel_center_integer = shader->pixel_center_integer;
}
linked_shader->Program->info.fs.early_fragment_tests |=
shader->EarlyFragmentTests || shader->PostDepthCoverage;
linked_shader->Program->info.fs.inner_coverage |= shader->InnerCoverage;
linked_shader->Program->info.fs.post_depth_coverage |=
shader->PostDepthCoverage;
linked_shader->Program->info.fs.pixel_interlock_ordered |=
shader->PixelInterlockOrdered;
linked_shader->Program->info.fs.pixel_interlock_unordered |=
shader->PixelInterlockUnordered;
linked_shader->Program->info.fs.sample_interlock_ordered |=
shader->SampleInterlockOrdered;
linked_shader->Program->info.fs.sample_interlock_unordered |=
shader->SampleInterlockUnordered;
linked_shader->Program->sh.fs.BlendSupport |= shader->BlendSupport;
}
linked_shader->Program->info.fs.pixel_center_integer = pixel_center_integer;
linked_shader->Program->info.fs.origin_upper_left = origin_upper_left;
}
static void
link_gs_inout_layout_qualifiers(struct gl_shader_program *prog,
struct gl_program *gl_prog,
struct gl_shader **shader_list,
unsigned num_shaders)
{
if (gl_prog->info.stage != MESA_SHADER_GEOMETRY ||
prog->data->Version < 150)
return;
int vertices_out = -1;
gl_prog->info.gs.invocations = 0;
gl_prog->info.gs.input_primitive = PRIM_UNKNOWN;
gl_prog->info.gs.output_primitive = PRIM_UNKNOWN;
for (unsigned i = 0; i < num_shaders; i++) {
struct gl_shader *shader = shader_list[i];
if (shader->info.Geom.InputType != PRIM_UNKNOWN) {
if (gl_prog->info.gs.input_primitive != PRIM_UNKNOWN &&
gl_prog->info.gs.input_primitive !=
shader->info.Geom.InputType) {
linker_error(prog, "geometry shader defined with conflicting "
"input types\n");
return;
}
gl_prog->info.gs.input_primitive = shader->info.Geom.InputType;
}
if (shader->info.Geom.OutputType != PRIM_UNKNOWN) {
if (gl_prog->info.gs.output_primitive != PRIM_UNKNOWN &&
gl_prog->info.gs.output_primitive !=
shader->info.Geom.OutputType) {
linker_error(prog, "geometry shader defined with conflicting "
"output types\n");
return;
}
gl_prog->info.gs.output_primitive = shader->info.Geom.OutputType;
}
if (shader->info.Geom.VerticesOut != -1) {
if (vertices_out != -1 &&
vertices_out != shader->info.Geom.VerticesOut) {
linker_error(prog, "geometry shader defined with conflicting "
"output vertex count (%d and %d)\n",
vertices_out, shader->info.Geom.VerticesOut);
return;
}
vertices_out = shader->info.Geom.VerticesOut;
}
if (shader->info.Geom.Invocations != 0) {
if (gl_prog->info.gs.invocations != 0 &&
gl_prog->info.gs.invocations !=
(unsigned) shader->info.Geom.Invocations) {
linker_error(prog, "geometry shader defined with conflicting "
"invocation count (%d and %d)\n",
gl_prog->info.gs.invocations,
shader->info.Geom.Invocations);
return;
}
gl_prog->info.gs.invocations = shader->info.Geom.Invocations;
}
}
if (gl_prog->info.gs.input_primitive == PRIM_UNKNOWN) {
linker_error(prog,
"geometry shader didn't declare primitive input type\n");
return;
}
if (gl_prog->info.gs.output_primitive == PRIM_UNKNOWN) {
linker_error(prog,
"geometry shader didn't declare primitive output type\n");
return;
}
if (vertices_out == -1) {
linker_error(prog,
"geometry shader didn't declare max_vertices\n");
return;
} else {
gl_prog->info.gs.vertices_out = vertices_out;
}
if (gl_prog->info.gs.invocations == 0)
gl_prog->info.gs.invocations = 1;
}
static void
link_cs_input_layout_qualifiers(struct gl_shader_program *prog,
struct gl_program *gl_prog,
struct gl_shader **shader_list,
unsigned num_shaders)
{
if (gl_prog->info.stage != MESA_SHADER_COMPUTE)
return;
for (int i = 0; i < 3; i++)
gl_prog->info.cs.local_size[i] = 0;
gl_prog->info.cs.local_size_variable = false;
gl_prog->info.cs.derivative_group = DERIVATIVE_GROUP_NONE;
for (unsigned sh = 0; sh < num_shaders; sh++) {
struct gl_shader *shader = shader_list[sh];
if (shader->info.Comp.LocalSize[0] != 0) {
if (gl_prog->info.cs.local_size[0] != 0) {
for (int i = 0; i < 3; i++) {
if (gl_prog->info.cs.local_size[i] !=
shader->info.Comp.LocalSize[i]) {
linker_error(prog, "compute shader defined with conflicting "
"local sizes\n");
return;
}
}
}
for (int i = 0; i < 3; i++) {
gl_prog->info.cs.local_size[i] =
shader->info.Comp.LocalSize[i];
}
} else if (shader->info.Comp.LocalSizeVariable) {
if (gl_prog->info.cs.local_size[0] != 0) {
linker_error(prog, "compute shader defined with both fixed and "
"variable local group size\n");
return;
}
gl_prog->info.cs.local_size_variable = true;
}
enum gl_derivative_group group = shader->info.Comp.DerivativeGroup;
if (group != DERIVATIVE_GROUP_NONE) {
if (gl_prog->info.cs.derivative_group != DERIVATIVE_GROUP_NONE &&
gl_prog->info.cs.derivative_group != group) {
linker_error(prog, "compute shader defined with conflicting "
"derivative groups\n");
return;
}
gl_prog->info.cs.derivative_group = group;
}
}
if (gl_prog->info.cs.local_size[0] == 0 &&
!gl_prog->info.cs.local_size_variable) {
linker_error(prog, "compute shader must contain a fixed or a variable "
"local group size\n");
return;
}
if (gl_prog->info.cs.derivative_group == DERIVATIVE_GROUP_QUADS) {
if (gl_prog->info.cs.local_size[0] % 2 != 0) {
linker_error(prog, "derivative_group_quadsNV must be used with a "
"local group size whose first dimension "
"is a multiple of 2\n");
return;
}
if (gl_prog->info.cs.local_size[1] % 2 != 0) {
linker_error(prog, "derivative_group_quadsNV must be used with a local"
"group size whose second dimension "
"is a multiple of 2\n");
return;
}
} else if (gl_prog->info.cs.derivative_group == DERIVATIVE_GROUP_LINEAR) {
if ((gl_prog->info.cs.local_size[0] *
gl_prog->info.cs.local_size[1] *
gl_prog->info.cs.local_size[2]) % 4 != 0) {
linker_error(prog, "derivative_group_linearNV must be used with a "
"local group size whose total number of invocations "
"is a multiple of 4\n");
return;
}
}
}
static void
link_output_variables(struct gl_linked_shader *linked_shader,
struct gl_shader **shader_list,
unsigned num_shaders)
{
struct glsl_symbol_table *symbols = linked_shader->symbols;
for (unsigned i = 0; i < num_shaders; i++) {
if (shader_list[i]->symbols->get_function("main"))
continue;
foreach_in_list(ir_instruction, ir, shader_list[i]->ir) {
if (ir->ir_type != ir_type_variable)
continue;
ir_variable *var = (ir_variable *) ir;
if (var->data.mode == ir_var_shader_out &&
!symbols->get_variable(var->name)) {
var = var->clone(linked_shader, NULL);
symbols->add_variable(var);
linked_shader->ir->push_head(var);
}
}
}
return;
}
struct gl_linked_shader *
link_intrastage_shaders(void *mem_ctx,
struct gl_context *ctx,
struct gl_shader_program *prog,
struct gl_shader **shader_list,
unsigned num_shaders,
bool allow_missing_main)
{
struct gl_uniform_block *ubo_blocks = NULL;
struct gl_uniform_block *ssbo_blocks = NULL;
unsigned num_ubo_blocks = 0;
unsigned num_ssbo_blocks = 0;
glsl_symbol_table variables;
for (unsigned i = 0; i < num_shaders; i++) {
if (shader_list[i] == NULL)
continue;
cross_validate_globals(ctx, prog, shader_list[i]->ir, &variables,
false);
}
if (!prog->data->LinkStatus)
return NULL;
validate_intrastage_interface_blocks(prog, (const gl_shader **)shader_list,
num_shaders);
if (!prog->data->LinkStatus)
return NULL;
for (unsigned i = 0; i < (num_shaders - 1); i++) {
foreach_in_list(ir_instruction, node, shader_list[i]->ir) {
ir_function *const f = node->as_function();
if (f == NULL)
continue;
for (unsigned j = i + 1; j < num_shaders; j++) {
ir_function *const other =
shader_list[j]->symbols->get_function(f->name);
if (other == NULL)
continue;
foreach_in_list(ir_function_signature, sig, &f->signatures) {
if (!sig->is_defined)
continue;
ir_function_signature *other_sig =
other->exact_matching_signature(NULL, &sig->parameters);
if (other_sig != NULL && other_sig->is_defined) {
linker_error(prog, "function `%s' is multiply defined\n",
f->name);
return NULL;
}
}
}
}
}
gl_shader *main = NULL;
for (unsigned i = 0; i < num_shaders; i++) {
if (_mesa_get_main_function_signature(shader_list[i]->symbols)) {
main = shader_list[i];
break;
}
}
if (main == NULL && allow_missing_main)
main = shader_list[0];
if (main == NULL) {
linker_error(prog, "%s shader lacks `main'\n",
_mesa_shader_stage_to_string(shader_list[0]->Stage));
return NULL;
}
gl_linked_shader *linked = rzalloc(NULL, struct gl_linked_shader);
linked->Stage = shader_list[0]->Stage;
struct gl_program *gl_prog =
ctx->Driver.NewProgram(ctx, shader_list[0]->Stage, prog->Name, false);
if (!gl_prog) {
prog->data->LinkStatus = LINKING_FAILURE;
_mesa_delete_linked_shader(ctx, linked);
return NULL;
}
_mesa_reference_shader_program_data(ctx, &gl_prog->sh.data, prog->data);
linked->Program = gl_prog;
linked->ir = new(linked) exec_list;
clone_ir_list(mem_ctx, linked->ir, main->ir);
link_fs_inout_layout_qualifiers(prog, linked, shader_list, num_shaders);
link_tcs_out_layout_qualifiers(prog, gl_prog, shader_list, num_shaders);
link_tes_in_layout_qualifiers(prog, gl_prog, shader_list, num_shaders);
link_gs_inout_layout_qualifiers(prog, gl_prog, shader_list, num_shaders);
link_cs_input_layout_qualifiers(prog, gl_prog, shader_list, num_shaders);
if (linked->Stage != MESA_SHADER_FRAGMENT)
link_xfb_stride_layout_qualifiers(ctx, prog, shader_list, num_shaders);
link_bindless_layout_qualifiers(prog, shader_list, num_shaders);
link_layer_viewport_relative_qualifier(prog, gl_prog, shader_list, num_shaders);
populate_symbol_table(linked, shader_list[0]->symbols);
ir_function_signature *const main_sig =
_mesa_get_main_function_signature(linked->symbols);
if (main_sig != NULL) {
exec_node *insertion_point =
move_non_declarations(linked->ir, (exec_node *) &main_sig->body, false,
linked);
for (unsigned i = 0; i < num_shaders; i++) {
if (shader_list[i] == main)
continue;
insertion_point = move_non_declarations(shader_list[i]->ir,
insertion_point, true, linked);
}
}
if (!link_function_calls(prog, linked, shader_list, num_shaders)) {
_mesa_delete_linked_shader(ctx, linked);
return NULL;
}
if (linked->Stage != MESA_SHADER_FRAGMENT)
link_output_variables(linked, shader_list, num_shaders);
array_sizing_visitor v;
v.run(linked->ir);
v.fixup_unnamed_interface_types();
link_uniform_blocks(mem_ctx, ctx, prog, linked, &ubo_blocks,
&num_ubo_blocks, &ssbo_blocks, &num_ssbo_blocks);
const unsigned max_uniform_blocks =
ctx->Const.Program[linked->Stage].MaxUniformBlocks;
if (num_ubo_blocks > max_uniform_blocks) {
linker_error(prog, "Too many %s uniform blocks (%d/%d)\n",
_mesa_shader_stage_to_string(linked->Stage),
num_ubo_blocks, max_uniform_blocks);
}
const unsigned max_shader_storage_blocks =
ctx->Const.Program[linked->Stage].MaxShaderStorageBlocks;
if (num_ssbo_blocks > max_shader_storage_blocks) {
linker_error(prog, "Too many %s shader storage blocks (%d/%d)\n",
_mesa_shader_stage_to_string(linked->Stage),
num_ssbo_blocks, max_shader_storage_blocks);
}
if (!prog->data->LinkStatus) {
_mesa_delete_linked_shader(ctx, linked);
return NULL;
}
linked->Program->sh.UniformBlocks =
ralloc_array(linked, gl_uniform_block *, num_ubo_blocks);
ralloc_steal(linked, ubo_blocks);
for (unsigned i = 0; i < num_ubo_blocks; i++) {
linked->Program->sh.UniformBlocks[i] = &ubo_blocks[i];
}
linked->Program->info.num_ubos = num_ubo_blocks;
linked->Program->sh.ShaderStorageBlocks =
ralloc_array(linked, gl_uniform_block *, num_ssbo_blocks);
ralloc_steal(linked, ssbo_blocks);
for (unsigned i = 0; i < num_ssbo_blocks; i++) {
linked->Program->sh.ShaderStorageBlocks[i] = &ssbo_blocks[i];
}
linked->Program->info.num_ssbos = num_ssbo_blocks;
validate_ir_tree(linked->ir);
if (linked->Stage == MESA_SHADER_GEOMETRY) {
unsigned num_vertices =
vertices_per_prim(gl_prog->info.gs.input_primitive);
array_resize_visitor input_resize_visitor(num_vertices, prog,
MESA_SHADER_GEOMETRY);
foreach_in_list(ir_instruction, ir, linked->ir) {
ir->accept(&input_resize_visitor);
}
}
if (ctx->Const.VertexID_is_zero_based)
lower_vertex_id(linked);
if (ctx->Const.LowerCsDerivedVariables)
lower_cs_derived(linked);
#ifdef DEBUG
linked->SourceChecksum = 0;
for (unsigned i = 0; i < num_shaders; i++) {
if (shader_list[i] == NULL)
continue;
linked->SourceChecksum ^= shader_list[i]->SourceChecksum;
}
#endif
return linked;
}
static void
update_array_sizes(struct gl_shader_program *prog)
{
for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
if (prog->_LinkedShaders[i] == NULL)
continue;
bool types_were_updated = false;
foreach_in_list(ir_instruction, node, prog->_LinkedShaders[i]->ir) {
ir_variable *const var = node->as_variable();
if ((var == NULL) || (var->data.mode != ir_var_uniform) ||
!var->type->is_array())
continue;
if (var->is_in_buffer_block() || var->type->contains_atomic() ||
var->type->contains_subroutine() || var->constant_initializer)
continue;
int size = var->data.max_array_access;
for (unsigned j = 0; j < MESA_SHADER_STAGES; j++) {
if (prog->_LinkedShaders[j] == NULL)
continue;
foreach_in_list(ir_instruction, node2, prog->_LinkedShaders[j]->ir) {
ir_variable *other_var = node2->as_variable();
if (!other_var)
continue;
if (strcmp(var->name, other_var->name) == 0 &&
other_var->data.max_array_access > size) {
size = other_var->data.max_array_access;
}
}
}
if (size + 1 != (int)var->type->length) {
const unsigned num_slots = var->get_num_state_slots();
if (num_slots > 0) {
var->set_num_state_slots((size + 1)
* (num_slots / var->type->length));
}
var->type = glsl_type::get_array_instance(var->type->fields.array,
size + 1);
types_were_updated = true;
}
}
if (types_were_updated) {
deref_type_updater v;
v.run(prog->_LinkedShaders[i]->ir);
}
}
}
static void
resize_tes_inputs(struct gl_context *ctx,
struct gl_shader_program *prog)
{
if (prog->_LinkedShaders[MESA_SHADER_TESS_EVAL] == NULL)
return;
gl_linked_shader *const tcs = prog->_LinkedShaders[MESA_SHADER_TESS_CTRL];
gl_linked_shader *const tes = prog->_LinkedShaders[MESA_SHADER_TESS_EVAL];
const int num_vertices = tcs
? tcs->Program->info.tess.tcs_vertices_out
: ctx->Const.MaxPatchVertices;
array_resize_visitor input_resize_visitor(num_vertices, prog,
MESA_SHADER_TESS_EVAL);
foreach_in_list(ir_instruction, ir, tes->ir) {
ir->accept(&input_resize_visitor);
}
if (tcs) {
foreach_in_list(ir_instruction, ir, tes->ir) {
ir_variable *var = ir->as_variable();
if (var && var->data.mode == ir_var_system_value &&
var->data.location == SYSTEM_VALUE_VERTICES_IN) {
void *mem_ctx = ralloc_parent(var);
var->data.location = 0;
var->data.explicit_location = false;
var->data.mode = ir_var_auto;
var->constant_value = new(mem_ctx) ir_constant(num_vertices);
}
}
}
}
static int
find_available_slots(unsigned used_mask, unsigned needed_count)
{
unsigned needed_mask = (1 << needed_count) - 1;
const int max_bit_to_test = (8 * sizeof(used_mask)) - needed_count;
if ((needed_count == 0) || (max_bit_to_test < 0) || (max_bit_to_test > 32))
return -1;
for (int i = 0; i <= max_bit_to_test; i++) {
if ((needed_mask & ~used_mask) == needed_mask)
return i;
needed_mask <<= 1;
}
return -1;
}
#define SAFE_MASK_FROM_INDEX(i) (((i) >= 32) ? ~0 : ((1 << (i)) - 1))
static bool
assign_attribute_or_color_locations(void *mem_ctx,
gl_shader_program *prog,
struct gl_constants *constants,
unsigned target_index,
bool do_assignment)
{
unsigned max_index = (target_index == MESA_SHADER_VERTEX) ?
constants->Program[target_index].MaxAttribs :
MAX2(constants->MaxDrawBuffers, constants->MaxDualSourceDrawBuffers);
unsigned used_locations = ~SAFE_MASK_FROM_INDEX(max_index);
unsigned double_storage_locations = 0;
assert((target_index == MESA_SHADER_VERTEX)
|| (target_index == MESA_SHADER_FRAGMENT));
gl_linked_shader *const sh = prog->_LinkedShaders[target_index];
if (sh == NULL)
return true;
const int generic_base = (target_index == MESA_SHADER_VERTEX)
? (int) VERT_ATTRIB_GENERIC0 : (int) FRAG_RESULT_DATA0;
const enum ir_variable_mode direction =
(target_index == MESA_SHADER_VERTEX)
? ir_var_shader_in : ir_var_shader_out;
struct temp_attr {
unsigned slots;
ir_variable *var;
static int compare(const void *a, const void *b)
{
const temp_attr *const l = (const temp_attr *) a;
const temp_attr *const r = (const temp_attr *) b;
return r->slots - l->slots;
}
} to_assign[32];
assert(max_index <= 32);
ir_variable *assigned[12 * 4];
unsigned assigned_attr = 0;
unsigned num_attr = 0;
foreach_in_list(ir_instruction, node, sh->ir) {
ir_variable *const var = node->as_variable();
if ((var == NULL) || (var->data.mode != (unsigned) direction))
continue;
if (var->data.explicit_location) {
var->data.is_unmatched_generic_inout = 0;
if ((var->data.location >= (int)(max_index + generic_base))
|| (var->data.location < 0)) {
linker_error(prog,
"invalid explicit location %d specified for `%s'\n",
(var->data.location < 0)
? var->data.location
: var->data.location - generic_base,
var->name);
return false;
}
} else if (target_index == MESA_SHADER_VERTEX) {
unsigned binding;
if (prog->AttributeBindings->get(binding, var->name)) {
assert(binding >= VERT_ATTRIB_GENERIC0);
var->data.location = binding;
var->data.is_unmatched_generic_inout = 0;
}
} else if (target_index == MESA_SHADER_FRAGMENT) {
unsigned binding;
unsigned index;
const char *name = var->name;
const glsl_type *type = var->type;
while (type) {
if (prog->FragDataBindings->get(binding, name)) {
assert(binding >= FRAG_RESULT_DATA0);
var->data.location = binding;
var->data.is_unmatched_generic_inout = 0;
if (prog->FragDataIndexBindings->get(index, name)) {
var->data.index = index;
}
break;
}
if (type->is_array()) {
name = ralloc_asprintf(mem_ctx, "%s[0]", name);
type = type->fields.array;
continue;
}
break;
}
}
if (strcmp(var->name, "gl_LastFragData") == 0)
continue;
if (target_index == MESA_SHADER_FRAGMENT && var->data.index >= 1 &&
var->data.location - generic_base >=
(int) constants->MaxDualSourceDrawBuffers) {
linker_error(prog,
"output location %d >= GL_MAX_DUAL_SOURCE_DRAW_BUFFERS "
"with index %u for %s\n",
var->data.location - generic_base, var->data.index,
var->name);
return false;
}
const unsigned slots = var->type->count_attribute_slots(target_index == MESA_SHADER_VERTEX);
if (var->data.location != -1) {
if (var->data.location >= generic_base && var->data.index < 1) {
const unsigned attr = var->data.location - generic_base;
const unsigned use_mask = (1 << slots) - 1;
const char *const string = (target_index == MESA_SHADER_VERTEX)
? "vertex shader input" : "fragment shader output";
if (attr + slots > max_index) {
linker_error(prog,
"insufficient contiguous locations "
"available for %s `%s' %d %d %d\n", string,
var->name, used_locations, use_mask, attr);
return false;
}
if ((~(use_mask << attr) & used_locations) != used_locations) {
if (target_index == MESA_SHADER_FRAGMENT && !prog->IsES) {
for (unsigned i = 0; i < assigned_attr; i++) {
unsigned assigned_slots =
assigned[i]->type->count_attribute_slots(false);
unsigned assig_attr =
assigned[i]->data.location - generic_base;
unsigned assigned_use_mask = (1 << assigned_slots) - 1;
if ((assigned_use_mask << assig_attr) &
(use_mask << attr)) {
const glsl_type *assigned_type =
assigned[i]->type->without_array();
const glsl_type *type = var->type->without_array();
if (assigned_type->base_type != type->base_type) {
linker_error(prog, "types do not match for aliased"
" %ss %s and %s\n", string,
assigned[i]->name, var->name);
return false;
}
unsigned assigned_component_mask =
((1 << assigned_type->vector_elements) - 1) <<
assigned[i]->data.location_frac;
unsigned component_mask =
((1 << type->vector_elements) - 1) <<
var->data.location_frac;
if (assigned_component_mask & component_mask) {
linker_error(prog, "overlapping component is "
"assigned to %ss %s and %s "
"(component=%d)\n",
string, assigned[i]->name, var->name,
var->data.location_frac);
return false;
}
}
}
} else if (target_index == MESA_SHADER_FRAGMENT ||
(prog->IsES && prog->data->Version >= 300)) {
linker_error(prog, "overlapping location is assigned "
"to %s `%s' %d %d %d\n", string, var->name,
used_locations, use_mask, attr);
return false;
} else {
linker_warning(prog, "overlapping location is assigned "
"to %s `%s' %d %d %d\n", string, var->name,
used_locations, use_mask, attr);
}
}
if (target_index == MESA_SHADER_FRAGMENT && !prog->IsES) {
assert(assigned_attr < ARRAY_SIZE(assigned));
assigned[assigned_attr] = var;
assigned_attr++;
}
used_locations |= (use_mask << attr);
if (var->type->without_array()->is_dual_slot())
double_storage_locations |= (use_mask << attr);
}
continue;
}
if (num_attr >= max_index) {
linker_error(prog, "too many %s (max %u)",
target_index == MESA_SHADER_VERTEX ?
"vertex shader inputs" : "fragment shader outputs",
max_index);
return false;
}
to_assign[num_attr].slots = slots;
to_assign[num_attr].var = var;
num_attr++;
}
if (!do_assignment)
return true;
if (target_index == MESA_SHADER_VERTEX) {
unsigned total_attribs_size =
util_bitcount(used_locations & SAFE_MASK_FROM_INDEX(max_index)) +
util_bitcount(double_storage_locations);
if (total_attribs_size > max_index) {
linker_error(prog,
"attempt to use %d vertex attribute slots only %d available ",
total_attribs_size, max_index);
return false;
}
}
if (num_attr == 0)
return true;
qsort(to_assign, num_attr, sizeof(to_assign[0]), temp_attr::compare);
if (target_index == MESA_SHADER_VERTEX) {
find_deref_visitor find("gl_Vertex");
find.run(sh->ir);
if (find.variable_found())
used_locations |= (1 << 0);
}
for (unsigned i = 0; i < num_attr; i++) {
const unsigned use_mask = (1 << to_assign[i].slots) - 1;
int location = find_available_slots(used_locations, to_assign[i].slots);
if (location < 0) {
const char *const string = (target_index == MESA_SHADER_VERTEX)
? "vertex shader input" : "fragment shader output";
linker_error(prog,
"insufficient contiguous locations "
"available for %s `%s'\n",
string, to_assign[i].var->name);
return false;
}
to_assign[i].var->data.location = generic_base + location;
to_assign[i].var->data.is_unmatched_generic_inout = 0;
used_locations |= (use_mask << location);
if (to_assign[i].var->type->without_array()->is_dual_slot())
double_storage_locations |= (use_mask << location);
}
if (target_index == MESA_SHADER_VERTEX) {
unsigned total_attribs_size =
util_bitcount(used_locations & SAFE_MASK_FROM_INDEX(max_index)) +
util_bitcount(double_storage_locations);
if (total_attribs_size > max_index) {
linker_error(prog,
"attempt to use %d vertex attribute slots only %d available ",
total_attribs_size, max_index);
return false;
}
}
return true;
}
static void
match_explicit_outputs_to_inputs(gl_linked_shader *producer,
gl_linked_shader *consumer)
{
glsl_symbol_table parameters;
ir_variable *explicit_locations[MAX_VARYINGS_INCL_PATCH][4] =
{ {NULL, NULL} };
foreach_in_list(ir_instruction, node, producer->ir) {
ir_variable *const var = node->as_variable();
if ((var == NULL) || (var->data.mode != ir_var_shader_out))
continue;
if (var->data.explicit_location &&
var->data.location >= VARYING_SLOT_VAR0) {
const unsigned idx = var->data.location - VARYING_SLOT_VAR0;
if (explicit_locations[idx][var->data.location_frac] == NULL)
explicit_locations[idx][var->data.location_frac] = var;
if (producer->Stage == MESA_SHADER_TESS_CTRL)
var->data.is_unmatched_generic_inout = 0;
}
}
foreach_in_list(ir_instruction, node, consumer->ir) {
ir_variable *const input = node->as_variable();
if ((input == NULL) || (input->data.mode != ir_var_shader_in))
continue;
ir_variable *output = NULL;
if (input->data.explicit_location
&& input->data.location >= VARYING_SLOT_VAR0) {
output = explicit_locations[input->data.location - VARYING_SLOT_VAR0]
[input->data.location_frac];
if (output != NULL){
input->data.is_unmatched_generic_inout = 0;
output->data.is_unmatched_generic_inout = 0;
}
}
}
}
static void
store_fragdepth_layout(struct gl_shader_program *prog)
{
if (prog->_LinkedShaders[MESA_SHADER_FRAGMENT] == NULL) {
return;
}
struct exec_list *ir = prog->_LinkedShaders[MESA_SHADER_FRAGMENT]->ir;
foreach_in_list(ir_instruction, node, ir) {
ir_variable *const var = node->as_variable();
if (var == NULL || var->data.mode != ir_var_shader_out) {
continue;
}
if (strcmp(var->name, "gl_FragDepth") == 0) {
switch (var->data.depth_layout) {
case ir_depth_layout_none:
prog->FragDepthLayout = FRAG_DEPTH_LAYOUT_NONE;
return;
case ir_depth_layout_any:
prog->FragDepthLayout = FRAG_DEPTH_LAYOUT_ANY;
return;
case ir_depth_layout_greater:
prog->FragDepthLayout = FRAG_DEPTH_LAYOUT_GREATER;
return;
case ir_depth_layout_less:
prog->FragDepthLayout = FRAG_DEPTH_LAYOUT_LESS;
return;
case ir_depth_layout_unchanged:
prog->FragDepthLayout = FRAG_DEPTH_LAYOUT_UNCHANGED;
return;
default:
assert(0);
return;
}
}
}
}
static void
check_image_resources(struct gl_context *ctx, 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)
return;
for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
struct gl_linked_shader *sh = prog->_LinkedShaders[i];
if (sh) {
total_image_units += sh->Program->info.num_images;
total_shader_storage_blocks += sh->Program->info.num_ssbos;
if (i == MESA_SHADER_FRAGMENT) {
foreach_in_list(ir_instruction, node, sh->ir) {
ir_variable *var = node->as_variable();
if (var && var->data.mode == ir_var_shader_out)
fragment_outputs += var->type->count_attribute_slots(false);
}
}
}
}
if (total_image_units > ctx->Const.MaxCombinedImageUniforms)
linker_error(prog, "Too many combined image uniforms\n");
if (total_image_units + fragment_outputs + total_shader_storage_blocks >
ctx->Const.MaxCombinedShaderOutputResources)
linker_error(prog, "Too many combined image uniforms, shader storage "
" buffers and fragment outputs\n");
}
static int
reserve_explicit_locations(struct gl_shader_program *prog,
string_to_uint_map *map, ir_variable *var)
{
unsigned slots = var->type->uniform_locations();
unsigned max_loc = var->data.location + slots - 1;
unsigned return_value = slots;
if (max_loc + 1 > prog->NumUniformRemapTable) {
prog->UniformRemapTable =
reralloc(prog, prog->UniformRemapTable,
gl_uniform_storage *,
max_loc + 1);
if (!prog->UniformRemapTable) {
linker_error(prog, "Out of memory during linking.\n");
return -1;
}
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;
if (prog->UniformRemapTable[loc] == INACTIVE_UNIFORM_EXPLICIT_LOCATION) {
unsigned hash_loc;
if (map->get(hash_loc, var->name) && hash_loc == loc - i) {
return_value = 0;
continue;
}
linker_error(prog,
"location qualifier for uniform %s overlaps "
"previously used location\n",
var->name);
return -1;
}
prog->UniformRemapTable[loc] = INACTIVE_UNIFORM_EXPLICIT_LOCATION;
}
map->put(var->data.location, var->name);
return return_value;
}
static bool
reserve_subroutine_explicit_locations(struct gl_shader_program *prog,
struct gl_program *p,
ir_variable *var)
{
unsigned slots = var->type->uniform_locations();
unsigned max_loc = var->data.location + slots - 1;
if (max_loc + 1 > p->sh.NumSubroutineUniformRemapTable) {
p->sh.SubroutineUniformRemapTable =
reralloc(p, p->sh.SubroutineUniformRemapTable,
gl_uniform_storage *,
max_loc + 1);
if (!p->sh.SubroutineUniformRemapTable) {
linker_error(prog, "Out of memory during linking.\n");
return false;
}
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;
if (p->sh.SubroutineUniformRemapTable[loc] == INACTIVE_UNIFORM_EXPLICIT_LOCATION) {
linker_error(prog,
"location qualifier for uniform %s overlaps "
"previously used location\n",
var->name);
return false;
}
p->sh.SubroutineUniformRemapTable[loc] = INACTIVE_UNIFORM_EXPLICIT_LOCATION;
}
return true;
}
static void
check_explicit_uniform_locations(struct gl_context *ctx,
struct gl_shader_program *prog)
{
prog->NumExplicitUniformLocations = 0;
if (!ctx->Extensions.ARB_explicit_uniform_location)
return;
string_to_uint_map *uniform_map = new string_to_uint_map;
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;
foreach_in_list(ir_instruction, node, prog->_LinkedShaders[i]->ir) {
ir_variable *var = node->as_variable();
if (!var || var->data.mode != ir_var_uniform)
continue;
if (var->data.explicit_location) {
bool ret = false;
if (var->type->without_array()->is_subroutine())
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) {
delete uniform_map;
return;
}
}
}
}
link_util_update_empty_uniform_locations(prog);
delete uniform_map;
prog->NumExplicitUniformLocations = entries_total;
}
static bool
included_in_packed_varying(ir_variable *var, const char *name)
{
if (strncmp(var->name, "packed:", 7) != 0)
return false;
char *list = strdup(var->name + 7);
assert(list);
bool found = false;
char *saveptr;
char *token = strtok_r(list, ",", &saveptr);
while (token) {
if (strcmp(token, name) == 0) {
found = true;
break;
}
token = strtok_r(NULL, ",", &saveptr);
}
free(list);
return found;
}
static uint8_t
build_stageref(struct gl_shader_program *shProg, const char *name,
unsigned mode)
{
uint8_t stages = 0;
assert(MESA_SHADER_STAGES < 8);
for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
struct gl_linked_shader *sh = shProg->_LinkedShaders[i];
if (!sh)
continue;
foreach_in_list(ir_instruction, node, sh->ir) {
ir_variable *var = node->as_variable();
if (var) {
unsigned baselen = strlen(var->name);
if (included_in_packed_varying(var, name)) {
stages |= (1 << i);
break;
}
if (var->data.mode != mode)
continue;
if (strncmp(var->name, name, baselen) == 0) {
if (name[baselen] == '\0' ||
name[baselen] == '[' ||
name[baselen] == '.') {
stages |= (1 << i);
break;
}
}
}
}
}
return stages;
}
static gl_shader_variable *
create_shader_variable(struct gl_shader_program *shProg,
const ir_variable *in,
const char *name, const glsl_type *type,
const glsl_type *interface_type,
bool use_implicit_location, int location,
const glsl_type *outermost_struct_type)
{
gl_shader_variable *out = rzalloc(shProg, struct gl_shader_variable);
if (!out)
return NULL;
if (in->data.mode == ir_var_system_value &&
in->data.location == SYSTEM_VALUE_VERTEX_ID_ZERO_BASE) {
out->name = ralloc_strdup(shProg, "gl_VertexID");
} else if ((in->data.mode == ir_var_shader_out &&
in->data.location == VARYING_SLOT_TESS_LEVEL_OUTER) ||
(in->data.mode == ir_var_system_value &&
in->data.location == SYSTEM_VALUE_TESS_LEVEL_OUTER)) {
out->name = ralloc_strdup(shProg, "gl_TessLevelOuter");
type = glsl_type::get_array_instance(glsl_type::float_type, 4);
} else if ((in->data.mode == ir_var_shader_out &&
in->data.location == VARYING_SLOT_TESS_LEVEL_INNER) ||
(in->data.mode == ir_var_system_value &&
in->data.location == SYSTEM_VALUE_TESS_LEVEL_INNER)) {
out->name = ralloc_strdup(shProg, "gl_TessLevelInner");
type = glsl_type::get_array_instance(glsl_type::float_type, 2);
} else {
out->name = ralloc_strdup(shProg, name);
}
if (!out->name)
return NULL;
if (in->type->is_atomic_uint() || is_gl_identifier(in->name) ||
!(in->data.explicit_location || use_implicit_location)) {
out->location = -1;
} else {
out->location = location;
}
out->type = type;
out->outermost_struct_type = outermost_struct_type;
out->interface_type = interface_type;
out->component = in->data.location_frac;
out->index = in->data.index;
out->patch = in->data.patch;
out->mode = in->data.mode;
out->interpolation = in->data.interpolation;
out->explicit_location = in->data.explicit_location;
out->precision = in->data.precision;
return out;
}
static bool
add_shader_variable(const struct gl_context *ctx,
struct gl_shader_program *shProg,
struct set *resource_set,
unsigned stage_mask,
GLenum programInterface, ir_variable *var,
const char *name, const glsl_type *type,
bool use_implicit_location, int location,
bool inouts_share_location,
const glsl_type *outermost_struct_type = NULL)
{
const glsl_type *interface_type = var->get_interface_type();
if (outermost_struct_type == NULL) {
if (var->data.from_named_ifc_block) {
const char *interface_name = interface_type->name;
if (interface_type->is_array()) {
type = type->fields.array;
interface_name = interface_type->fields.array->name;
}
name = ralloc_asprintf(shProg, "%s.%s", interface_name, name);
}
}
switch (type->base_type) {
case GLSL_TYPE_STRUCT: {
if (outermost_struct_type == NULL)
outermost_struct_type = type;
unsigned field_location = location;
for (unsigned i = 0; i < type->length; i++) {
const struct glsl_struct_field *field = &type->fields.structure[i];
char *field_name = ralloc_asprintf(shProg, "%s.%s", name, field->name);
if (!add_shader_variable(ctx, shProg, resource_set,
stage_mask, programInterface,
var, field_name, field->type,
use_implicit_location, field_location,
false, outermost_struct_type))
return false;
field_location += field->type->count_attribute_slots(false);
}
return true;
}
case GLSL_TYPE_ARRAY: {
const struct glsl_type *array_type = type->fields.array;
if (array_type->base_type == GLSL_TYPE_STRUCT ||
array_type->base_type == GLSL_TYPE_ARRAY) {
unsigned elem_location = location;
unsigned stride = inouts_share_location ? 0 :
array_type->count_attribute_slots(false);
for (unsigned i = 0; i < type->length; i++) {
char *elem = ralloc_asprintf(shProg, "%s[%d]", name, i);
if (!add_shader_variable(ctx, shProg, resource_set,
stage_mask, programInterface,
var, elem, array_type,
use_implicit_location, elem_location,
false, outermost_struct_type))
return false;
elem_location += stride;
}
return true;
}
}
default: {
gl_shader_variable *sha_v =
create_shader_variable(shProg, var, name, type, interface_type,
use_implicit_location, location,
outermost_struct_type);
if (!sha_v)
return false;
return link_util_add_program_resource(shProg, resource_set,
programInterface, sha_v, stage_mask);
}
}
}
static bool
inout_has_same_location(const ir_variable *var, unsigned stage)
{
if (!var->data.patch &&
((var->data.mode == ir_var_shader_out &&
stage == MESA_SHADER_TESS_CTRL) ||
(var->data.mode == ir_var_shader_in &&
(stage == MESA_SHADER_TESS_CTRL || stage == MESA_SHADER_TESS_EVAL ||
stage == MESA_SHADER_GEOMETRY))))
return true;
else
return false;
}
static bool
add_interface_variables(const struct gl_context *ctx,
struct gl_shader_program *shProg,
struct set *resource_set,
unsigned stage, GLenum programInterface)
{
exec_list *ir = shProg->_LinkedShaders[stage]->ir;
foreach_in_list(ir_instruction, node, ir) {
ir_variable *var = node->as_variable();
if (!var || var->data.how_declared == ir_var_hidden)
continue;
int loc_bias;
switch (var->data.mode) {
case ir_var_system_value:
case ir_var_shader_in:
if (programInterface != GL_PROGRAM_INPUT)
continue;
loc_bias = (stage == MESA_SHADER_VERTEX) ? int(VERT_ATTRIB_GENERIC0)
: int(VARYING_SLOT_VAR0);
break;
case ir_var_shader_out:
if (programInterface != GL_PROGRAM_OUTPUT)
continue;
loc_bias = (stage == MESA_SHADER_FRAGMENT) ? int(FRAG_RESULT_DATA0)
: int(VARYING_SLOT_VAR0);
break;
default:
continue;
};
if (var->data.patch)
loc_bias = int(VARYING_SLOT_PATCH0);
if (strncmp(var->name, "packed:", 7) == 0)
continue;
if (strncmp(var->name, "gl_out_FragData", 15) == 0)
continue;
const bool vs_input_or_fs_output =
(stage == MESA_SHADER_VERTEX && var->data.mode == ir_var_shader_in) ||
(stage == MESA_SHADER_FRAGMENT && var->data.mode == ir_var_shader_out);
if (!add_shader_variable(ctx, shProg, resource_set,
1 << stage, programInterface,
var, var->name, var->type, vs_input_or_fs_output,
var->data.location - loc_bias,
inout_has_same_location(var, stage)))
return false;
}
return true;
}
static bool
add_packed_varyings(const struct gl_context *ctx,
struct gl_shader_program *shProg,
struct set *resource_set,
int stage, GLenum type)
{
struct gl_linked_shader *sh = shProg->_LinkedShaders[stage];
GLenum iface;
if (!sh || !sh->packed_varyings)
return true;
foreach_in_list(ir_instruction, node, sh->packed_varyings) {
ir_variable *var = node->as_variable();
if (var) {
switch (var->data.mode) {
case ir_var_shader_in:
iface = GL_PROGRAM_INPUT;
break;
case ir_var_shader_out:
iface = GL_PROGRAM_OUTPUT;
break;
default:
UNREACHABLE("unexpected type");
}
if (type == iface) {
const int stage_mask =
build_stageref(shProg, var->name, var->data.mode);
if (!add_shader_variable(ctx, shProg, resource_set,
stage_mask,
iface, var, var->name, var->type, false,
var->data.location - VARYING_SLOT_VAR0,
inout_has_same_location(var, stage)))
return false;
}
}
}
return true;
}
static bool
add_fragdata_arrays(const struct gl_context *ctx,
struct gl_shader_program *shProg,
struct set *resource_set)
{
struct gl_linked_shader *sh = shProg->_LinkedShaders[MESA_SHADER_FRAGMENT];
if (!sh || !sh->fragdata_arrays)
return true;
foreach_in_list(ir_instruction, node, sh->fragdata_arrays) {
ir_variable *var = node->as_variable();
if (var) {
assert(var->data.mode == ir_var_shader_out);
if (!add_shader_variable(ctx, shProg, resource_set,
1 << MESA_SHADER_FRAGMENT,
GL_PROGRAM_OUTPUT, var, var->name, var->type,
true, var->data.location - FRAG_RESULT_DATA0,
false))
return false;
}
}
return true;
}
void
build_program_resource_list(struct gl_context *ctx,
struct gl_shader_program *shProg,
bool add_packed_varyings_only)
{
if (shProg->data->ProgramResourceList) {
ralloc_free(shProg->data->ProgramResourceList);
shProg->data->ProgramResourceList = NULL;
shProg->data->NumProgramResourceList = 0;
}
int input_stage = MESA_SHADER_STAGES, output_stage = 0;
for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
if (!shProg->_LinkedShaders[i])
continue;
if (input_stage == MESA_SHADER_STAGES)
input_stage = i;
output_stage = i;
}
if (input_stage == MESA_SHADER_STAGES && output_stage == 0)
return;
struct set *resource_set = _mesa_pointer_set_create(NULL);
if (shProg->SeparateShader) {
if (!add_packed_varyings(ctx, shProg, resource_set,
input_stage, GL_PROGRAM_INPUT))
return;
if (!add_packed_varyings(ctx, shProg, resource_set,
output_stage, GL_PROGRAM_OUTPUT))
return;
}
if (add_packed_varyings_only) {
_mesa_set_destroy(resource_set, NULL);
return;
}
if (!add_fragdata_arrays(ctx, shProg, resource_set))
return;
if (!add_interface_variables(ctx, shProg, resource_set,
input_stage, GL_PROGRAM_INPUT))
return;
if (!add_interface_variables(ctx, shProg, resource_set,
output_stage, GL_PROGRAM_OUTPUT))
return;
if (shProg->last_vert_prog) {
struct gl_transform_feedback_info *linked_xfb =
shProg->last_vert_prog->sh.LinkedTransformFeedback;
if (linked_xfb->NumVarying > 0) {
for (int i = 0; i < linked_xfb->NumVarying; i++) {
if (!link_util_add_program_resource(shProg, resource_set,
GL_TRANSFORM_FEEDBACK_VARYING,
&linked_xfb->Varyings[i], 0))
return;
}
}
for (unsigned i = 0; i < ctx->Const.MaxTransformFeedbackBuffers; i++) {
if ((linked_xfb->ActiveBuffers >> i) & 1) {
linked_xfb->Buffers[i].Binding = i;
if (!link_util_add_program_resource(shProg, resource_set,
GL_TRANSFORM_FEEDBACK_BUFFER,
&linked_xfb->Buffers[i], 0))
return;
}
}
}
int top_level_array_base_offset = -1;
int top_level_array_size_in_bytes = -1;
int second_element_offset = -1;
int buffer_block_index = -1;
for (unsigned i = 0; i < shProg->data->NumUniformStorage; i++) {
if (shProg->data->UniformStorage[i].hidden)
continue;
bool is_shader_storage =
shProg->data->UniformStorage[i].is_shader_storage;
GLenum type = is_shader_storage ? GL_BUFFER_VARIABLE : GL_UNIFORM;
if (!link_util_should_add_buffer_variable(shProg,
&shProg->data->UniformStorage[i],
top_level_array_base_offset,
top_level_array_size_in_bytes,
second_element_offset,
buffer_block_index))
continue;
if (is_shader_storage) {
if (shProg->data->UniformStorage[i].offset >= second_element_offset) {
top_level_array_base_offset =
shProg->data->UniformStorage[i].offset;
top_level_array_size_in_bytes =
shProg->data->UniformStorage[i].top_level_array_size *
shProg->data->UniformStorage[i].top_level_array_stride;
second_element_offset = top_level_array_size_in_bytes ?
top_level_array_base_offset +
shProg->data->UniformStorage[i].top_level_array_stride : -1;
}
buffer_block_index = shProg->data->UniformStorage[i].block_index;
}
uint8_t stageref = shProg->data->UniformStorage[i].active_shader_mask;
if (!link_util_add_program_resource(shProg, resource_set, type,
&shProg->data->UniformStorage[i], stageref))
return;
}
for (unsigned i = 0; i < shProg->data->NumUniformBlocks; i++) {
if (!link_util_add_program_resource(shProg, resource_set, GL_UNIFORM_BLOCK,
&shProg->data->UniformBlocks[i], 0))
return;
}
for (unsigned i = 0; i < shProg->data->NumShaderStorageBlocks; i++) {
if (!link_util_add_program_resource(shProg, resource_set, GL_SHADER_STORAGE_BLOCK,
&shProg->data->ShaderStorageBlocks[i], 0))
return;
}
for (unsigned i = 0; i < shProg->data->NumAtomicBuffers; i++) {
if (!link_util_add_program_resource(shProg, resource_set, GL_ATOMIC_COUNTER_BUFFER,
&shProg->data->AtomicBuffers[i], 0))
return;
}
for (unsigned i = 0; i < shProg->data->NumUniformStorage; i++) {
GLenum type;
if (!shProg->data->UniformStorage[i].hidden)
continue;
for (int j = MESA_SHADER_VERTEX; j < MESA_SHADER_STAGES; j++) {
if (!shProg->data->UniformStorage[i].opaque[j].active ||
!shProg->data->UniformStorage[i].type->is_subroutine())
continue;
type = _mesa_shader_stage_to_subroutine_uniform((gl_shader_stage)j);
if (!link_util_add_program_resource(shProg, resource_set,
type, &shProg->data->UniformStorage[i], 0))
return;
}
}
unsigned mask = shProg->data->linked_stages;
while (mask) {
const int i = u_bit_scan(&mask);
struct gl_program *p = shProg->_LinkedShaders[i]->Program;
GLuint type = _mesa_shader_stage_to_subroutine((gl_shader_stage)i);
for (unsigned j = 0; j < p->sh.NumSubroutineFunctions; j++) {
if (!link_util_add_program_resource(shProg, resource_set,
type, &p->sh.SubroutineFunctions[j], 0))
return;
}
}
_mesa_set_destroy(resource_set, NULL);
}
static bool
validate_sampler_array_indexing(struct gl_context *ctx,
struct gl_shader_program *prog)
{
dynamic_sampler_array_indexing_visitor v;
for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
if (prog->_LinkedShaders[i] == NULL)
continue;
bool no_dynamic_indexing =
ctx->Const.ShaderCompilerOptions[i].EmitNoIndirectSampler;
v.run(prog->_LinkedShaders[i]->ir);
if (v.uses_dynamic_sampler_array_indexing()) {
const char *msg = "sampler arrays indexed with non-constant "
"expressions is forbidden in GLSL %s %u";
if (no_dynamic_indexing) {
linker_error(prog, msg, prog->IsES ? "ES" : "",
prog->data->Version);
return false;
} else {
linker_warning(prog, msg, prog->IsES ? "ES" : "",
prog->data->Version);
}
}
}
return true;
}
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);
gl_program *p = prog->_LinkedShaders[i]->Program;
p->sh.MaxSubroutineFunctionIndex = 0;
foreach_in_list(ir_instruction, node, prog->_LinkedShaders[i]->ir) {
ir_function *fn = node->as_function();
if (!fn)
continue;
if (fn->is_subroutine)
p->sh.NumSubroutineUniformTypes++;
if (!fn->num_subroutine_types)
continue;
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 = ralloc_strdup(p, fn->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);
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++;
}
}
}
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);
gl_program *p = prog->_LinkedShaders[i]->Program;
glsl_symbol_table *symbols = prog->_LinkedShaders[i]->symbols;
for (unsigned j = 0; j < p->sh.NumSubroutineFunctions; j++) {
unsigned definitions = 0;
char *name = p->sh.SubroutineFunctions[j].name;
ir_function *fn = symbols->get_function(name);
foreach_in_list(ir_function_signature, sig, &fn->signatures) {
if (sig->is_defined) {
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;
}
}
}
}
}
}
static void
set_always_active_io(exec_list *ir, ir_variable_mode io_mode)
{
assert(io_mode == ir_var_shader_in || io_mode == ir_var_shader_out);
foreach_in_list(ir_instruction, node, ir) {
ir_variable *const var = node->as_variable();
if (var == NULL || var->data.mode != io_mode)
continue;
if (var->data.how_declared == ir_var_declared_implicitly)
continue;
var->data.always_active_io = true;
}
}
static void
disable_varying_optimizations_for_sso(struct gl_shader_program *prog)
{
unsigned first, last;
assert(prog->SeparateShader);
first = MESA_SHADER_STAGES;
last = 0;
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++) {
gl_linked_shader *sh = prog->_LinkedShaders[stage];
if (!sh)
continue;
if (stage == first && stage != MESA_SHADER_VERTEX)
set_always_active_io(sh->ir, ir_var_shader_in);
if (stage == last && stage != MESA_SHADER_FRAGMENT)
set_always_active_io(sh->ir, ir_var_shader_out);
}
}
static void
link_and_validate_uniforms(struct gl_context *ctx,
struct gl_shader_program *prog)
{
update_array_sizes(prog);
if (!ctx->Const.UseNIRGLSLLinker) {
link_assign_uniform_locations(prog, ctx);
if (prog->data->LinkStatus == LINKING_FAILURE)
return;
link_util_calculate_subroutine_compat(prog);
link_util_check_uniform_resources(ctx, prog);
link_util_check_subroutine_resources(prog);
check_image_resources(ctx, prog);
link_assign_atomic_counter_resources(ctx, prog);
link_check_atomic_counter_resources(ctx, prog);
}
}
static bool
link_varyings_and_uniforms(unsigned first, unsigned last,
struct gl_context *ctx,
struct gl_shader_program *prog, void *mem_ctx)
{
for (unsigned i = MESA_SHADER_VERTEX; i <= MESA_SHADER_FRAGMENT; i++) {
if (prog->_LinkedShaders[i] != NULL) {
link_invalidate_variable_locations(prog->_LinkedShaders[i]->ir);
}
}
unsigned prev = first;
for (unsigned i = prev + 1; i <= MESA_SHADER_FRAGMENT; i++) {
if (prog->_LinkedShaders[i] == NULL)
continue;
match_explicit_outputs_to_inputs(prog->_LinkedShaders[prev],
prog->_LinkedShaders[i]);
prev = i;
}
if (!assign_attribute_or_color_locations(mem_ctx, prog, &ctx->Const,
MESA_SHADER_VERTEX, true)) {
return false;
}
if (!assign_attribute_or_color_locations(mem_ctx, prog, &ctx->Const,
MESA_SHADER_FRAGMENT, true)) {
return false;
}
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;
}
if (!link_varyings(prog, first, last, ctx, mem_ctx))
return false;
link_and_validate_uniforms(ctx, prog);
if (!prog->data->LinkStatus)
return false;
for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
if (prog->_LinkedShaders[i] == NULL)
continue;
const struct gl_shader_compiler_options *options =
&ctx->Const.ShaderCompilerOptions[i];
if (options->LowerBufferInterfaceBlocks)
lower_ubo_reference(prog->_LinkedShaders[i],
options->ClampBlockIndicesToArrayBounds,
ctx->Const.UseSTD430AsDefaultPacking);
if (i == MESA_SHADER_COMPUTE)
lower_shared_reference(ctx, prog, prog->_LinkedShaders[i]);
lower_vector_derefs(prog->_LinkedShaders[i]);
do_vec_index_to_swizzle(prog->_LinkedShaders[i]->ir);
}
return true;
}
static void
linker_optimisation_loop(struct gl_context *ctx, exec_list *ir,
unsigned stage)
{
if (ctx->Const.GLSLOptimizeConservatively) {
do_common_optimization(ir, true, false,
&ctx->Const.ShaderCompilerOptions[stage],
ctx->Const.NativeIntegers);
} else {
while (do_common_optimization(ir, true, false,
&ctx->Const.ShaderCompilerOptions[stage],
ctx->Const.NativeIntegers))
;
}
}
void
link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
{
prog->data->LinkStatus = LINKING_SUCCESS;
prog->data->Validated = false;
if (prog->NumShaders == 0) {
if (ctx->API != API_OPENGL_COMPAT)
linker_error(prog, "no shaders attached to the program\n");
return;
}
#ifdef ENABLE_SHADER_CACHE
if (shader_cache_read_program_metadata(ctx, prog))
return;
#endif
void *mem_ctx = ralloc_context(NULL);
prog->ARB_fragment_coord_conventions_enable = false;
struct gl_shader **shader_list[MESA_SHADER_STAGES];
unsigned num_shaders[MESA_SHADER_STAGES];
for (int i = 0; i < MESA_SHADER_STAGES; i++) {
shader_list[i] = (struct gl_shader **)
calloc(prog->NumShaders, sizeof(struct gl_shader *));
num_shaders[i] = 0;
}
unsigned min_version = UINT_MAX;
unsigned max_version = 0;
for (unsigned i = 0; i < prog->NumShaders; i++) {
min_version = MIN2(min_version, prog->Shaders[i]->Version);
max_version = MAX2(max_version, prog->Shaders[i]->Version);
if (!ctx->Const.AllowGLSLRelaxedES &&
prog->Shaders[i]->IsES != prog->Shaders[0]->IsES) {
linker_error(prog, "all shaders must use same shading "
"language version\n");
goto done;
}
if (prog->Shaders[i]->ARB_fragment_coord_conventions_enable) {
prog->ARB_fragment_coord_conventions_enable = true;
}
gl_shader_stage shader_type = prog->Shaders[i]->Stage;
shader_list[shader_type][num_shaders[shader_type]] = prog->Shaders[i];
num_shaders[shader_type]++;
}
if (!ctx->Const.AllowGLSLRelaxedES && prog->Shaders[0]->IsES &&
min_version != max_version) {
linker_error(prog, "all shaders must use same shading "
"language version\n");
goto done;
}
prog->data->Version = max_version;
prog->IsES = prog->Shaders[0]->IsES;
if (!prog->SeparateShader) {
if (num_shaders[MESA_SHADER_GEOMETRY] > 0 &&
num_shaders[MESA_SHADER_VERTEX] == 0) {
linker_error(prog, "Geometry shader must be linked with "
"vertex shader\n");
goto done;
}
if (num_shaders[MESA_SHADER_TESS_EVAL] > 0 &&
num_shaders[MESA_SHADER_VERTEX] == 0) {
linker_error(prog, "Tessellation evaluation shader must be linked "
"with vertex shader\n");
goto done;
}
if (num_shaders[MESA_SHADER_TESS_CTRL] > 0 &&
num_shaders[MESA_SHADER_VERTEX] == 0) {
linker_error(prog, "Tessellation control shader must be linked with "
"vertex shader\n");
goto done;
}
if (num_shaders[MESA_SHADER_TESS_CTRL] > 0 &&
num_shaders[MESA_SHADER_TESS_EVAL] == 0) {
linker_error(prog, "Tessellation control shader must be linked with "
"tessellation evaluation shader\n");
goto done;
}
if (prog->IsES) {
if (num_shaders[MESA_SHADER_TESS_EVAL] > 0 &&
num_shaders[MESA_SHADER_TESS_CTRL] == 0) {
linker_error(prog, "GLSL ES requires non-separable programs "
"containing a tessellation evaluation shader to also "
"be linked with a tessellation control shader\n");
goto done;
}
}
}
if (num_shaders[MESA_SHADER_COMPUTE] > 0 &&
num_shaders[MESA_SHADER_COMPUTE] != prog->NumShaders) {
linker_error(prog, "Compute shaders may not be linked with any other "
"type of shader\n");
}
for (int stage = 0; stage < MESA_SHADER_STAGES; stage++) {
if (num_shaders[stage] > 0) {
gl_linked_shader *const sh =
link_intrastage_shaders(mem_ctx, ctx, prog, shader_list[stage],
num_shaders[stage], false);
if (!prog->data->LinkStatus) {
if (sh)
_mesa_delete_linked_shader(ctx, sh);
goto done;
}
switch (stage) {
case MESA_SHADER_VERTEX:
validate_vertex_shader_executable(prog, sh, ctx);
break;
case MESA_SHADER_TESS_CTRL:
break;
case MESA_SHADER_TESS_EVAL:
validate_tess_eval_shader_executable(prog, sh, ctx);
break;
case MESA_SHADER_GEOMETRY:
validate_geometry_shader_executable(prog, sh, ctx);
break;
case MESA_SHADER_FRAGMENT:
validate_fragment_shader_executable(prog, sh);
break;
}
if (!prog->data->LinkStatus) {
if (sh)
_mesa_delete_linked_shader(ctx, sh);
goto done;
}
prog->_LinkedShaders[stage] = sh;
prog->data->linked_stages |= 1 << stage;
}
}
cross_validate_uniforms(ctx, prog);
if (!prog->data->LinkStatus)
goto done;
unsigned first, last, prev;
first = MESA_SHADER_STAGES;
last = 0;
for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
if (!prog->_LinkedShaders[i])
continue;
if (first == MESA_SHADER_STAGES)
first = i;
last = i;
}
check_explicit_uniform_locations(ctx, prog);
link_assign_subroutine_types(prog);
verify_subroutine_associated_funcs(prog);
if (!prog->data->LinkStatus)
goto done;
resize_tes_inputs(ctx, prog);
prev = first;
for (unsigned i = prev + 1; i <= MESA_SHADER_FRAGMENT; i++) {
if (prog->_LinkedShaders[i] == NULL)
continue;
validate_interstage_inout_blocks(prog, prog->_LinkedShaders[prev],
prog->_LinkedShaders[i]);
if (!prog->data->LinkStatus)
goto done;
cross_validate_outputs_to_inputs(ctx, prog,
prog->_LinkedShaders[prev],
prog->_LinkedShaders[i]);
if (!prog->data->LinkStatus)
goto done;
prev = i;
}
validate_first_and_last_interface_explicit_locations(ctx, prog,
(gl_shader_stage) first,
(gl_shader_stage) last);
validate_interstage_uniform_blocks(prog, prog->_LinkedShaders);
if (!prog->data->LinkStatus)
goto done;
for (unsigned int i = 0; i < MESA_SHADER_STAGES; i++) {
if (prog->_LinkedShaders[i] != NULL)
lower_named_interface_blocks(mem_ctx, prog->_LinkedShaders[i]);
}
if (prog->IsES && prog->data->Version == 100)
if (!validate_invariant_builtins(prog,
prog->_LinkedShaders[MESA_SHADER_VERTEX],
prog->_LinkedShaders[MESA_SHADER_FRAGMENT]))
goto done;
if (max_version >= (prog->IsES ? 300 : 130)) {
struct gl_linked_shader *sh = prog->_LinkedShaders[MESA_SHADER_FRAGMENT];
if (sh) {
lower_discard_flow(sh->ir);
}
}
if (prog->SeparateShader)
disable_varying_optimizations_for_sso(prog);
if (!interstage_cross_validate_uniform_blocks(prog, false))
goto done;
if (!interstage_cross_validate_uniform_blocks(prog, true))
goto done;
for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
if (prog->_LinkedShaders[i] == NULL)
continue;
detect_recursion_linked(prog, prog->_LinkedShaders[i]->ir);
if (!prog->data->LinkStatus)
goto done;
if (ctx->Const.ShaderCompilerOptions[i].LowerCombinedClipCullDistance) {
lower_clip_cull_distance(prog, prog->_LinkedShaders[i]);
}
if (ctx->Const.LowerTessLevel) {
lower_tess_level(prog->_LinkedShaders[i]);
}
if (prog->IsES && i == MESA_SHADER_VERTEX) {
if (!assign_attribute_or_color_locations(mem_ctx, prog, &ctx->Const,
MESA_SHADER_VERTEX, false)) {
goto done;
}
}
linker_optimisation_loop(ctx, prog->_LinkedShaders[i]->ir, i);
if (ctx->Const.GLSLLowerConstArrays &&
lower_const_arrays_to_uniforms(prog->_LinkedShaders[i]->ir, i,
ctx->Const.Program[i].MaxUniformComponents))
linker_optimisation_loop(ctx, prog->_LinkedShaders[i]->ir, i);
}
if ((!prog->IsES && prog->data->Version < 130) ||
(prog->IsES && prog->data->Version < 300)) {
if (!validate_sampler_array_indexing(ctx, prog))
goto done;
}
validate_geometry_shader_emissions(ctx, prog);
store_fragdepth_layout(prog);
if(!link_varyings_and_uniforms(first, last, ctx, prog, mem_ctx))
goto done;
for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
if (prog->_LinkedShaders[i] == NULL)
continue;
optimize_swizzles(prog->_LinkedShaders[i]->ir);
}
if (!prog->SeparateShader && ctx->API == API_OPENGLES2 &&
num_shaders[MESA_SHADER_COMPUTE] == 0) {
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");
}
}
done:
for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
free(shader_list[i]);
if (prog->_LinkedShaders[i] == NULL)
continue;
validate_ir_tree(prog->_LinkedShaders[i]->ir);
reparent_ir(prog->_LinkedShaders[i]->ir, prog->_LinkedShaders[i]->ir);
delete prog->_LinkedShaders[i]->symbols;
prog->_LinkedShaders[i]->symbols = NULL;
}
ralloc_free(mem_ctx);
}