virtual ir_visitor_status visit_enter(ir_call *ir)
{
- exec_list_iterator sig_iter = ir->get_callee()->parameters.iterator();
+ exec_list_iterator sig_iter = ir->callee->parameters.iterator();
foreach_iter(exec_list_iterator, iter, *ir) {
ir_rvalue *param_rval = (ir_rvalue *)iter.get();
ir_variable *sig_param = (ir_variable *)sig_iter.get();
sig_iter.next();
}
+ if (ir->return_deref != NULL) {
+ ir_variable *const var = ir->return_deref->variable_referenced();
+
+ if (strcmp(name, var->name) == 0) {
+ found = true;
+ return visit_stop;
+ }
+ }
+
return visit_continue_with_parent;
}
/**
* Verify that a vertex shader executable meets all semantic requirements.
*
- * Also sets prog->Vert.UsesClipDistance as a side effect.
+ * Also sets prog->Vert.UsesClipDistance and prog->Vert.ClipDistanceArraySize
+ * as a side effect.
*
* \param shader Vertex shader executable to be verified
*/
if (shader == NULL)
return true;
- find_assignment_visitor find("gl_Position");
- find.run(shader->ir);
- if (!find.variable_found()) {
- linker_error(prog, "vertex shader does not write to `gl_Position'\n");
- return false;
+ /* From the GLSL 1.10 spec, page 48:
+ *
+ * "The variable gl_Position is available only in the vertex
+ * language and is intended for writing the homogeneous vertex
+ * position. All executions of a well-formed vertex shader
+ * executable must write a value into this variable. [...] The
+ * variable gl_Position is available only in the vertex
+ * language and is intended for writing the homogeneous vertex
+ * position. All executions of a well-formed vertex shader
+ * executable must write a value into this variable."
+ *
+ * while in GLSL 1.40 this text is changed to:
+ *
+ * "The variable gl_Position is available only in the vertex
+ * language and is intended for writing the homogeneous vertex
+ * position. It can be written at any time during shader
+ * execution. It may also be read back by a vertex shader
+ * after being written. This value will be used by primitive
+ * assembly, clipping, culling, and other fixed functionality
+ * operations, if present, that operate on primitives after
+ * vertex processing has occurred. Its value is undefined if
+ * the vertex shader executable does not write gl_Position."
+ */
+ if (prog->Version < 140) {
+ find_assignment_visitor find("gl_Position");
+ find.run(shader->ir);
+ if (!find.variable_found()) {
+ linker_error(prog, "vertex shader does not write to `gl_Position'\n");
+ return false;
+ }
}
+ prog->Vert.ClipDistanceArraySize = 0;
+
if (prog->Version >= 130) {
/* From section 7.1 (Vertex Shader Special Variables) of the
* GLSL 1.30 spec:
return false;
}
prog->Vert.UsesClipDistance = clip_distance.variable_found();
+ ir_variable *clip_distance_var =
+ shader->symbols->get_variable("gl_ClipDistance");
+ if (clip_distance_var)
+ prog->Vert.ClipDistanceArraySize = clip_distance_var->type->length;
}
return true;
MESA_SHADER_TYPES, true);
}
+/**
+ * Accumulates the array of prog->UniformBlocks and checks that all
+ * definitons of blocks agree on their contents.
+ */
+static bool
+interstage_cross_validate_uniform_blocks(struct gl_shader_program *prog)
+{
+ unsigned max_num_uniform_blocks = 0;
+ for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) {
+ if (prog->_LinkedShaders[i])
+ max_num_uniform_blocks += prog->_LinkedShaders[i]->NumUniformBlocks;
+ }
+
+ for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) {
+ struct gl_shader *sh = prog->_LinkedShaders[i];
+
+ prog->UniformBlockStageIndex[i] = ralloc_array(prog, int,
+ max_num_uniform_blocks);
+ for (unsigned int j = 0; j < max_num_uniform_blocks; j++)
+ prog->UniformBlockStageIndex[i][j] = -1;
+
+ if (sh == NULL)
+ continue;
+
+ for (unsigned int j = 0; j < sh->NumUniformBlocks; j++) {
+ int index = link_cross_validate_uniform_block(prog,
+ &prog->UniformBlocks,
+ &prog->NumUniformBlocks,
+ &sh->UniformBlocks[j]);
+
+ if (index == -1) {
+ linker_error(prog, "uniform block `%s' has mismatching definitions",
+ sh->UniformBlocks[j].Name);
+ return false;
+ }
+
+ prog->UniformBlockStageIndex[i][index] = j;
+ }
+ }
+
+ return true;
+}
/**
* Validate that outputs from one stage match inputs of another
continue;
assert(inst->as_assignment()
+ || inst->as_call()
|| ((var != NULL) && (var->mode == ir_var_temporary)));
if (make_copies) {
/**
+ * This class is only used in link_intrastage_shaders() below but declaring
+ * it inside that function leads to compiler warnings with some versions of
+ * gcc.
+ */
+class array_sizing_visitor : public ir_hierarchical_visitor {
+public:
+ virtual ir_visitor_status visit(ir_variable *var)
+ {
+ if (var->type->is_array() && (var->type->length == 0)) {
+ const glsl_type *type =
+ glsl_type::get_array_instance(var->type->fields.array,
+ var->max_array_access + 1);
+ assert(type != NULL);
+ var->type = type;
+ }
+ return visit_continue;
+ }
+};
+
+/**
* Combine a group of shaders for a single stage to generate a linked shader
*
* \note
struct gl_shader **shader_list,
unsigned num_shaders)
{
+ struct gl_uniform_block *uniform_blocks = NULL;
+ unsigned num_uniform_blocks = 0;
+
/* Check that global variables defined in multiple shaders are consistent.
*/
if (!cross_validate_globals(prog, shader_list, num_shaders, false))
return NULL;
+ /* Check that uniform blocks between shaders for a stage agree. */
+ for (unsigned i = 0; i < num_shaders; i++) {
+ struct gl_shader *sh = shader_list[i];
+
+ for (unsigned j = 0; j < shader_list[i]->NumUniformBlocks; j++) {
+ link_assign_uniform_block_offsets(shader_list[i]);
+
+ int index = link_cross_validate_uniform_block(mem_ctx,
+ &uniform_blocks,
+ &num_uniform_blocks,
+ &sh->UniformBlocks[j]);
+ if (index == -1) {
+ linker_error(prog, "uniform block `%s' has mismatching definitions",
+ sh->UniformBlocks[j].Name);
+ return NULL;
+ }
+ }
+ }
+
/* Check that there is only a single definition of each function signature
* across all shaders.
*/
linked->ir = new(linked) exec_list;
clone_ir_list(mem_ctx, linked->ir, main->ir);
+ linked->UniformBlocks = uniform_blocks;
+ linked->NumUniformBlocks = num_uniform_blocks;
+ ralloc_steal(linked, linked->UniformBlocks);
+
populate_symbol_table(linked);
/* The a pointer to the main function in the final linked shader (i.e., the
* max_array_access field.
*/
if (linked != NULL) {
- class array_sizing_visitor : public ir_hierarchical_visitor {
- public:
- virtual ir_visitor_status visit(ir_variable *var)
- {
- if (var->type->is_array() && (var->type->length == 0)) {
- const glsl_type *type =
- glsl_type::get_array_instance(var->type->fields.array,
- var->max_array_access + 1);
-
- assert(type != NULL);
- var->type = type;
- }
-
- return visit_continue;
- }
- } v;
+ array_sizing_visitor v;
v.run(linked->ir);
}
!var->type->is_array())
continue;
+ /* GL_ARB_uniform_buffer_object says that std140 uniforms
+ * will not be eliminated. Since we always do std140, just
+ * don't resize arrays in UBOs.
+ */
+ if (var->uniform_block != -1)
+ continue;
+
unsigned int size = var->max_array_access;
for (unsigned j = 0; j < MESA_SHADER_TYPES; j++) {
if (prog->_LinkedShaders[j] == NULL)
}
} else if (target_index == MESA_SHADER_FRAGMENT) {
unsigned binding;
+ unsigned index;
if (prog->FragDataBindings->get(binding, var->name)) {
assert(binding >= FRAG_RESULT_DATA0);
var->location = binding;
+
+ if (prog->FragDataIndexBindings->get(index, var->name)) {
+ var->index = index;
+ }
}
}
*/
const unsigned slots = count_attribute_slots(var->type);
if (var->location != -1) {
- if (var->location >= generic_base) {
+ if (var->location >= generic_base && var->index < 1) {
/* From page 61 of the OpenGL 4.0 spec:
*
* "LinkProgram will fail if the attribute bindings assigned
* attribute overlaps any previously allocated bits.
*/
if ((~(use_mask << attr) & used_locations) != used_locations) {
+ const char *const string = (target_index == MESA_SHADER_VERTEX)
+ ? "vertex shader input" : "fragment shader output";
linker_error(prog,
- "insufficient contiguous attribute locations "
- "available for vertex shader input `%s'",
- var->name);
+ "insufficient contiguous locations "
+ "available for %s `%s' %d %d %d", string,
+ var->name, used_locations, use_mask, attr);
return false;
}
? "vertex shader input" : "fragment shader output";
linker_error(prog,
- "insufficient contiguous attribute locations "
+ "insufficient contiguous locations "
"available for %s `%s'",
string, to_assign[i].var->name);
return false;
class tfeedback_decl
{
public:
- bool init(struct gl_shader_program *prog, const void *mem_ctx,
- const char *input);
+ bool init(struct gl_context *ctx, struct gl_shader_program *prog,
+ const void *mem_ctx, const char *input);
static bool is_same(const tfeedback_decl &x, const tfeedback_decl &y);
bool assign_location(struct gl_context *ctx, struct gl_shader_program *prog,
ir_variable *output_var);
- bool store(struct gl_shader_program *prog,
- struct gl_transform_feedback_info *info, unsigned buffer) const;
-
+ bool accumulate_num_outputs(struct gl_shader_program *prog, unsigned *count);
+ bool store(struct gl_context *ctx, struct gl_shader_program *prog,
+ struct gl_transform_feedback_info *info, unsigned buffer,
+ const unsigned max_outputs) const;
/**
* True if assign_location() has been called for this object.
return this->location != -1;
}
+ bool is_next_buffer_separator() const
+ {
+ return this->next_buffer_separator;
+ }
+
+ bool is_varying() const
+ {
+ return !this->next_buffer_separator && !this->skip_components;
+ }
+
/**
* Determine whether this object refers to the variable var.
*/
bool matches_var(ir_variable *var) const
{
- return strcmp(var->name, this->var_name) == 0;
+ if (this->is_clip_distance_mesa)
+ return strcmp(var->name, "gl_ClipDistanceMESA") == 0;
+ else
+ return strcmp(var->name, this->var_name) == 0;
}
/**
*/
unsigned num_components() const
{
- return this->vector_elements * this->matrix_columns;
+ if (this->is_clip_distance_mesa)
+ return this->size;
+ else
+ return this->vector_elements * this->matrix_columns * this->size;
}
private:
/**
* The name that was supplied to glTransformFeedbackVaryings. Used for
- * error reporting.
+ * error reporting and glGetTransformFeedbackVarying().
*/
const char *orig_name;
/**
* The name of the variable, parsed from orig_name.
*/
- char *var_name;
+ const char *var_name;
/**
* True if the declaration in orig_name represents an array.
*/
- bool is_array;
+ bool is_subscripted;
/**
- * If is_array is true, the array index that was specified in orig_name.
+ * If is_subscripted is true, the subscript that was specified in orig_name.
*/
- unsigned array_index;
+ unsigned array_subscript;
+
+ /**
+ * True if the variable is gl_ClipDistance and the driver lowers
+ * gl_ClipDistance to gl_ClipDistanceMESA.
+ */
+ bool is_clip_distance_mesa;
/**
* The vertex shader output location that the linker assigned for this
* if this variable is not a matrix.
*/
unsigned matrix_columns;
+
+ /** Type of the varying returned by glGetTransformFeedbackVarying() */
+ GLenum type;
+
+ /**
+ * If location != -1, the size that should be returned by
+ * glGetTransformFeedbackVarying().
+ */
+ unsigned size;
+
+ /**
+ * How many components to skip. If non-zero, this is
+ * gl_SkipComponents{1,2,3,4} from ARB_transform_feedback3.
+ */
+ unsigned skip_components;
+
+ /**
+ * Whether this is gl_NextBuffer from ARB_transform_feedback3.
+ */
+ bool next_buffer_separator;
};
* reported using linker_error(), and false is returned.
*/
bool
-tfeedback_decl::init(struct gl_shader_program *prog, const void *mem_ctx,
- const char *input)
+tfeedback_decl::init(struct gl_context *ctx, struct gl_shader_program *prog,
+ const void *mem_ctx, const char *input)
{
/* We don't have to be pedantic about what is a valid GLSL variable name,
* because any variable with an invalid name can't exist in the IR anyway.
this->location = -1;
this->orig_name = input;
+ this->is_clip_distance_mesa = false;
+ this->skip_components = 0;
+ this->next_buffer_separator = false;
+
+ if (ctx->Extensions.ARB_transform_feedback3) {
+ /* Parse gl_NextBuffer. */
+ if (strcmp(input, "gl_NextBuffer") == 0) {
+ this->next_buffer_separator = true;
+ return true;
+ }
+
+ /* Parse gl_SkipComponents. */
+ if (strcmp(input, "gl_SkipComponents1") == 0)
+ this->skip_components = 1;
+ else if (strcmp(input, "gl_SkipComponents2") == 0)
+ this->skip_components = 2;
+ else if (strcmp(input, "gl_SkipComponents3") == 0)
+ this->skip_components = 3;
+ else if (strcmp(input, "gl_SkipComponents4") == 0)
+ this->skip_components = 4;
+
+ if (this->skip_components)
+ return true;
+ }
+ /* Parse a declaration. */
const char *bracket = strrchr(input, '[');
if (bracket) {
this->var_name = ralloc_strndup(mem_ctx, input, bracket - input);
- if (sscanf(bracket, "[%u]", &this->array_index) == 1) {
- this->is_array = true;
- return true;
+ if (sscanf(bracket, "[%u]", &this->array_subscript) != 1) {
+ linker_error(prog, "Cannot parse transform feedback varying %s", input);
+ return false;
}
+ this->is_subscripted = true;
} else {
this->var_name = ralloc_strdup(mem_ctx, input);
- this->is_array = false;
- return true;
+ this->is_subscripted = false;
}
- linker_error(prog, "Cannot parse transform feedback varying %s", input);
- return false;
+ /* For drivers that lower gl_ClipDistance to gl_ClipDistanceMESA, this
+ * class must behave specially to account for the fact that gl_ClipDistance
+ * is converted from a float[8] to a vec4[2].
+ */
+ if (ctx->ShaderCompilerOptions[MESA_SHADER_VERTEX].LowerClipDistance &&
+ strcmp(this->var_name, "gl_ClipDistance") == 0) {
+ this->is_clip_distance_mesa = true;
+ }
+
+ return true;
}
bool
tfeedback_decl::is_same(const tfeedback_decl &x, const tfeedback_decl &y)
{
+ assert(x.is_varying() && y.is_varying());
+
if (strcmp(x.var_name, y.var_name) != 0)
return false;
- if (x.is_array != y.is_array)
+ if (x.is_subscripted != y.is_subscripted)
return false;
- if (x.is_array && x.array_index != y.array_index)
+ if (x.is_subscripted && x.array_subscript != y.array_subscript)
return false;
return true;
}
struct gl_shader_program *prog,
ir_variable *output_var)
{
+ assert(this->is_varying());
+
if (output_var->type->is_array()) {
/* Array variable */
- if (!this->is_array) {
- linker_error(prog, "Transform feedback varying %s found, "
- "but it's not an array ([] not expected).",
- this->orig_name);
- return false;
- }
- /* Check array bounds. */
- if (this->array_index >=
- (unsigned) output_var->type->array_size()) {
- linker_error(prog, "Transform feedback varying %s has index "
- "%i, but the array size is %i.",
- this->orig_name, this->array_index,
- output_var->type->array_size());
- return false;
- }
const unsigned matrix_cols =
output_var->type->fields.array->matrix_columns;
- this->location = output_var->location + this->array_index * matrix_cols;
+ unsigned actual_array_size = this->is_clip_distance_mesa ?
+ prog->Vert.ClipDistanceArraySize : output_var->type->array_size();
+
+ if (this->is_subscripted) {
+ /* Check array bounds. */
+ if (this->array_subscript >= actual_array_size) {
+ linker_error(prog, "Transform feedback varying %s has index "
+ "%i, but the array size is %u.",
+ this->orig_name, this->array_subscript,
+ actual_array_size);
+ return false;
+ }
+ if (this->is_clip_distance_mesa) {
+ this->location =
+ output_var->location + this->array_subscript / 4;
+ } else {
+ this->location =
+ output_var->location + this->array_subscript * matrix_cols;
+ }
+ this->size = 1;
+ } else {
+ this->location = output_var->location;
+ this->size = actual_array_size;
+ }
this->vector_elements = output_var->type->fields.array->vector_elements;
this->matrix_columns = matrix_cols;
+ if (this->is_clip_distance_mesa)
+ this->type = GL_FLOAT;
+ else
+ this->type = output_var->type->fields.array->gl_type;
} else {
/* Regular variable (scalar, vector, or matrix) */
- if (this->is_array) {
- linker_error(prog, "Transform feedback varying %s found, "
- "but it's an array ([] expected).",
- this->orig_name);
+ if (this->is_subscripted) {
+ linker_error(prog, "Transform feedback varying %s requested, "
+ "but %s is not an array.",
+ this->orig_name, this->var_name);
return false;
}
this->location = output_var->location;
+ this->size = 1;
this->vector_elements = output_var->type->vector_elements;
this->matrix_columns = output_var->type->matrix_columns;
+ this->type = output_var->type->gl_type;
}
+
/* From GL_EXT_transform_feedback:
* A program will fail to link if:
*
}
-/**
- * Update gl_transform_feedback_info to reflect this tfeedback_decl.
- *
- * If an error occurs, the error is reported through linker_error() and false
- * is returned.
- */
bool
-tfeedback_decl::store(struct gl_shader_program *prog,
- struct gl_transform_feedback_info *info,
- unsigned buffer) const
+tfeedback_decl::accumulate_num_outputs(struct gl_shader_program *prog,
+ unsigned *count)
{
+ if (!this->is_varying()) {
+ return true;
+ }
+
if (!this->is_assigned()) {
/* From GL_EXT_transform_feedback:
* A program will fail to link if:
this->orig_name);
return false;
}
- for (unsigned v = 0; v < this->matrix_columns; ++v) {
- info->Outputs[info->NumOutputs].OutputRegister = this->location + v;
- info->Outputs[info->NumOutputs].NumComponents = this->vector_elements;
- info->Outputs[info->NumOutputs].OutputBuffer = buffer;
- info->Outputs[info->NumOutputs].DstOffset = info->BufferStride[buffer];
- ++info->NumOutputs;
- info->BufferStride[buffer] += this->vector_elements;
+
+ unsigned translated_size = this->size;
+ if (this->is_clip_distance_mesa)
+ translated_size = (translated_size + 3) / 4;
+
+ *count += translated_size * this->matrix_columns;
+
+ return true;
+}
+
+
+/**
+ * Update gl_transform_feedback_info to reflect this tfeedback_decl.
+ *
+ * If an error occurs, the error is reported through linker_error() and false
+ * is returned.
+ */
+bool
+tfeedback_decl::store(struct gl_context *ctx, struct gl_shader_program *prog,
+ struct gl_transform_feedback_info *info,
+ unsigned buffer, const unsigned max_outputs) const
+{
+ assert(!this->next_buffer_separator);
+
+ /* Handle gl_SkipComponents. */
+ if (this->skip_components) {
+ info->BufferStride[buffer] += this->skip_components;
+ return true;
+ }
+
+ /* From GL_EXT_transform_feedback:
+ * A program will fail to link if:
+ *
+ * * the total number of components to capture is greater than
+ * the constant MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_EXT
+ * and the buffer mode is INTERLEAVED_ATTRIBS_EXT.
+ */
+ if (prog->TransformFeedback.BufferMode == GL_INTERLEAVED_ATTRIBS &&
+ info->BufferStride[buffer] + this->num_components() >
+ ctx->Const.MaxTransformFeedbackInterleavedComponents) {
+ linker_error(prog, "The MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS "
+ "limit has been exceeded.");
+ return false;
+ }
+
+ unsigned translated_size = this->size;
+ if (this->is_clip_distance_mesa)
+ translated_size = (translated_size + 3) / 4;
+ unsigned components_so_far = 0;
+ for (unsigned index = 0; index < translated_size; ++index) {
+ for (unsigned v = 0; v < this->matrix_columns; ++v) {
+ unsigned num_components = this->vector_elements;
+ assert(info->NumOutputs < max_outputs);
+ info->Outputs[info->NumOutputs].ComponentOffset = 0;
+ if (this->is_clip_distance_mesa) {
+ if (this->is_subscripted) {
+ num_components = 1;
+ info->Outputs[info->NumOutputs].ComponentOffset =
+ this->array_subscript % 4;
+ } else {
+ num_components = MIN2(4, this->size - components_so_far);
+ }
+ }
+ info->Outputs[info->NumOutputs].OutputRegister =
+ this->location + v + index * this->matrix_columns;
+ info->Outputs[info->NumOutputs].NumComponents = num_components;
+ info->Outputs[info->NumOutputs].OutputBuffer = buffer;
+ info->Outputs[info->NumOutputs].DstOffset = info->BufferStride[buffer];
+ ++info->NumOutputs;
+ info->BufferStride[buffer] += num_components;
+ components_so_far += num_components;
+ }
}
+ assert(components_so_far == this->num_components());
+
+ info->Varyings[info->NumVarying].Name = ralloc_strdup(prog, this->orig_name);
+ info->Varyings[info->NumVarying].Type = this->type;
+ info->Varyings[info->NumVarying].Size = this->size;
+ info->NumVarying++;
+
return true;
}
* is returned.
*/
static bool
-parse_tfeedback_decls(struct gl_shader_program *prog, const void *mem_ctx,
- unsigned num_names, char **varying_names,
- tfeedback_decl *decls)
+parse_tfeedback_decls(struct gl_context *ctx, struct gl_shader_program *prog,
+ const void *mem_ctx, unsigned num_names,
+ char **varying_names, tfeedback_decl *decls)
{
for (unsigned i = 0; i < num_names; ++i) {
- if (!decls[i].init(prog, mem_ctx, varying_names[i]))
+ if (!decls[i].init(ctx, prog, mem_ctx, varying_names[i]))
return false;
+
+ if (!decls[i].is_varying())
+ continue;
+
/* From GL_EXT_transform_feedback:
* A program will fail to link if:
*
* feedback of arrays would be useless otherwise.
*/
for (unsigned j = 0; j < i; ++j) {
+ if (!decls[j].is_varying())
+ continue;
+
if (tfeedback_decl::is_same(decls[i], decls[j])) {
linker_error(prog, "Transform feedback varying %s specified "
"more than once.", varying_names[i]);
/**
+ * Is the given variable a varying variable to be counted against the
+ * limit in ctx->Const.MaxVarying?
+ * This includes variables such as texcoords, colors and generic
+ * varyings, but excludes variables such as gl_FrontFacing and gl_FragCoord.
+ */
+static bool
+is_varying_var(GLenum shaderType, const ir_variable *var)
+{
+ /* Only fragment shaders will take a varying variable as an input */
+ if (shaderType == GL_FRAGMENT_SHADER &&
+ var->mode == ir_var_in &&
+ var->explicit_location) {
+ switch (var->location) {
+ case FRAG_ATTRIB_WPOS:
+ case FRAG_ATTRIB_FACE:
+ case FRAG_ATTRIB_PNTC:
+ return false;
+ default:
+ return true;
+ }
+ }
+ return false;
+}
+
+
+/**
* Assign locations for all variables that are produced in one pipeline stage
* (the "producer") and consumed in the next stage (the "consumer").
*
}
for (unsigned i = 0; i < num_tfeedback_decls; ++i) {
+ if (!tfeedback_decls[i].is_varying())
+ continue;
+
if (!tfeedback_decls[i].is_assigned() &&
tfeedback_decls[i].matches_var(output_var)) {
if (output_var->location == -1) {
* value is written by the previous stage.
*/
var->mode = ir_var_auto;
- } else {
+ } else if (is_varying_var(consumer->Type, var)) {
/* The packing rules are used for vertex shader inputs are also
* used for fragment shader inputs.
*/
unsigned num_tfeedback_decls,
tfeedback_decl *tfeedback_decls)
{
- unsigned total_tfeedback_components = 0;
+ bool separate_attribs_mode =
+ prog->TransformFeedback.BufferMode == GL_SEPARATE_ATTRIBS;
+
+ ralloc_free(prog->LinkedTransformFeedback.Varyings);
+ ralloc_free(prog->LinkedTransformFeedback.Outputs);
+
memset(&prog->LinkedTransformFeedback, 0,
sizeof(prog->LinkedTransformFeedback));
- for (unsigned i = 0; i < num_tfeedback_decls; ++i) {
- unsigned buffer =
- prog->TransformFeedback.BufferMode == GL_SEPARATE_ATTRIBS ? i : 0;
- if (!tfeedback_decls[i].store(prog, &prog->LinkedTransformFeedback,
- buffer))
+
+ prog->LinkedTransformFeedback.Varyings =
+ rzalloc_array(prog,
+ struct gl_transform_feedback_varying_info,
+ num_tfeedback_decls);
+
+ unsigned num_outputs = 0;
+ for (unsigned i = 0; i < num_tfeedback_decls; ++i)
+ if (!tfeedback_decls[i].accumulate_num_outputs(prog, &num_outputs))
return false;
- total_tfeedback_components += tfeedback_decls[i].num_components();
+
+ prog->LinkedTransformFeedback.Outputs =
+ rzalloc_array(prog,
+ struct gl_transform_feedback_output,
+ num_outputs);
+
+ unsigned num_buffers = 0;
+
+ if (separate_attribs_mode) {
+ /* GL_SEPARATE_ATTRIBS */
+ for (unsigned i = 0; i < num_tfeedback_decls; ++i) {
+ if (!tfeedback_decls[i].store(ctx, prog, &prog->LinkedTransformFeedback,
+ num_buffers, num_outputs))
+ return false;
+
+ num_buffers++;
+ }
}
+ else {
+ /* GL_INVERLEAVED_ATTRIBS */
+ for (unsigned i = 0; i < num_tfeedback_decls; ++i) {
+ if (tfeedback_decls[i].is_next_buffer_separator()) {
+ num_buffers++;
+ continue;
+ }
- /* From GL_EXT_transform_feedback:
- * A program will fail to link if:
- *
- * * the total number of components to capture is greater than
- * the constant MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_EXT
- * and the buffer mode is INTERLEAVED_ATTRIBS_EXT.
- */
- if (prog->TransformFeedback.BufferMode == GL_INTERLEAVED_ATTRIBS &&
- total_tfeedback_components >
- ctx->Const.MaxTransformFeedbackInterleavedComponents) {
- linker_error(prog, "The MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS "
- "limit has been exceeded.");
- return false;
+ if (!tfeedback_decls[i].store(ctx, prog,
+ &prog->LinkedTransformFeedback,
+ num_buffers, num_outputs))
+ return false;
+ }
+ num_buffers++;
}
+ assert(prog->LinkedTransformFeedback.NumOutputs == num_outputs);
+
+ prog->LinkedTransformFeedback.NumBuffers = num_buffers;
return true;
}
prog->Validated = false;
prog->_Used = false;
- if (prog->InfoLog != NULL)
- ralloc_free(prog->InfoLog);
-
+ ralloc_free(prog->InfoLog);
prog->InfoLog = ralloc_strdup(NULL, "");
+ ralloc_free(prog->UniformBlocks);
+ prog->UniformBlocks = NULL;
+ prog->NumUniformBlocks = 0;
+ for (int i = 0; i < MESA_SHADER_TYPES; i++) {
+ ralloc_free(prog->UniformBlockStageIndex[i]);
+ prog->UniformBlockStageIndex[i] = NULL;
+ }
+
/* Separate the shaders into groups based on their type.
*/
struct gl_shader **vert_shader_list;
* of all shaders must match.
*/
assert(min_version >= 100);
- assert(max_version <= 130);
+ assert(max_version <= 140);
if ((max_version >= 130 || min_version == 100)
&& min_version != max_version) {
linker_error(prog, "all shaders must use same shading "
prog->LinkStatus = true;
}
+ /* Implement the GLSL 1.30+ rule for discard vs infinite loops Do
+ * it before optimization because we want most of the checks to get
+ * dropped thanks to constant propagation.
+ */
+ if (max_version >= 130) {
+ struct gl_shader *sh = prog->_LinkedShaders[MESA_SHADER_FRAGMENT];
+ if (sh) {
+ lower_discard_flow(sh->ir);
+ }
+ }
+
+ if (!interstage_cross_validate_uniform_blocks(prog))
+ goto done;
+
/* Do common optimization before assigning storage for attributes,
* uniforms, and varyings. Later optimization could possibly make
* some of that unused.
if (ctx->ShaderCompilerOptions[i].LowerClipDistance)
lower_clip_distance(prog->_LinkedShaders[i]->ir);
- while (do_common_optimization(prog->_LinkedShaders[i]->ir, true, false, 32))
+ unsigned max_unroll = ctx->ShaderCompilerOptions[i].MaxUnrollIterations;
+
+ while (do_common_optimization(prog->_LinkedShaders[i]->ir, true, false, max_unroll))
;
}
goto done;
}
- if (!assign_attribute_or_color_locations(prog, MESA_SHADER_FRAGMENT, ctx->Const.MaxDrawBuffers)) {
+ if (!assign_attribute_or_color_locations(prog, MESA_SHADER_FRAGMENT, MAX2(ctx->Const.MaxDrawBuffers, ctx->Const.MaxDualSourceDrawBuffers))) {
goto done;
}
tfeedback_decls = ralloc_array(mem_ctx, tfeedback_decl,
prog->TransformFeedback.NumVarying);
- if (!parse_tfeedback_decls(prog, mem_ctx, num_tfeedback_decls,
+ if (!parse_tfeedback_decls(ctx, prog, mem_ctx, num_tfeedback_decls,
prog->TransformFeedback.VaryingNames,
tfeedback_decls))
goto done;