#include <cstdlib>
#include <cstdio>
#include <cstdarg>
+#include <climits>
extern "C" {
#include <talloc.h>
}
-#include "main/mtypes.h"
+#include "main/core.h"
#include "glsl_symbol_table.h"
-#include "glsl_parser_extras.h"
#include "ir.h"
-#include "ir_optimization.h"
#include "program.h"
-#include "hash_table.h"
-#include "shader_api.h"
+#include "program/hash_table.h"
+#include "linker.h"
+#include "ir_optimization.h"
/**
* Visitor that determines whether or not a variable is ever written.
return visit_continue_with_parent;
}
+ virtual ir_visitor_status visit_enter(ir_call *ir)
+ {
+ exec_list_iterator sig_iter = ir->get_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();
+
+ if (sig_param->mode == ir_var_out ||
+ sig_param->mode == ir_var_inout) {
+ ir_variable *var = param_rval->variable_referenced();
+ if (var && strcmp(name, var->name) == 0) {
+ found = true;
+ return visit_stop;
+ }
+ }
+ sig_iter.next();
+ }
+
+ return visit_continue_with_parent;
+ }
+
bool variable_found()
{
return found;
};
+/**
+ * Visitor that determines whether or not a variable is ever read.
+ */
+class find_deref_visitor : public ir_hierarchical_visitor {
+public:
+ find_deref_visitor(const char *name)
+ : name(name), found(false)
+ {
+ /* empty */
+ }
+
+ 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; /**< Find writes to a variable with this name. */
+ bool found; /**< Was a write to the variable found? */
+};
+
+
void
linker_error_printf(gl_shader_program *prog, const char *fmt, ...)
{
/* Only assign locations for generic attributes / varyings / etc.
*/
- if (var->location >= generic_base)
+ if ((var->location >= generic_base) && !var->explicit_location)
var->location = -1;
}
}
if (shader == NULL)
return true;
- if (!shader->symbols->get_function("main")) {
- linker_error_printf(prog, "vertex shader lacks `main'\n");
- return false;
- }
-
find_assignment_visitor find("gl_Position");
find.run(shader->ir);
if (!find.variable_found()) {
if (shader == NULL)
return true;
- if (!shader->symbols->get_function("main")) {
- linker_error_printf(prog, "fragment shader lacks `main'\n");
- return false;
- }
-
find_assignment_visitor frag_color("gl_FragColor");
find_assignment_visitor frag_data("gl_FragData");
case ir_var_in: return "shader input";
case ir_var_out: return "shader output";
case ir_var_inout: return "shader inout";
+
+ case ir_var_temporary:
default:
assert(!"Should not get here.");
return "invalid variable";
if (uniforms_only && (var->mode != ir_var_uniform))
continue;
+ /* Don't cross validate temporaries that are at global scope. These
+ * will eventually get pulled into the shaders 'main'.
+ */
+ if (var->mode == ir_var_temporary)
+ continue;
+
/* If a global with this name has already been seen, verify that the
* new instance has the same type. In addition, if the globals have
* initializers, the values of the initializers must be the same.
ir_variable *const existing = variables.get_variable(var->name);
if (existing != NULL) {
if (var->type != existing->type) {
- linker_error_printf(prog, "%s `%s' declared as type "
- "`%s' and type `%s'\n",
- mode_string(var),
- var->name, var->type->name,
- existing->type->name);
- return false;
+ /* Consider the types to be "the same" if both types are arrays
+ * of the same type and one of the arrays is implicitly sized.
+ * In addition, set the type of the linked variable to the
+ * explicitly sized array.
+ */
+ if (var->type->is_array()
+ && existing->type->is_array()
+ && (var->type->fields.array == existing->type->fields.array)
+ && ((var->type->length == 0)
+ || (existing->type->length == 0))) {
+ if (existing->type->length == 0)
+ existing->type = var->type;
+ } else {
+ linker_error_printf(prog, "%s `%s' declared as type "
+ "`%s' and type `%s'\n",
+ mode_string(var),
+ var->name, var->type->name,
+ existing->type->name);
+ return false;
+ }
+ }
+
+ if (var->explicit_location) {
+ if (existing->explicit_location
+ && (var->location != existing->location)) {
+ linker_error_printf(prog, "explicit locations for %s "
+ "`%s' have differing values\n",
+ mode_string(var), var->name);
+ return false;
+ }
+
+ existing->location = var->location;
+ existing->explicit_location = true;
}
/* FINISHME: Handle non-constant initializers.
* have an initializer but a later instance does, copy the
* initializer to the version stored in the symbol table.
*/
+ /* FINISHME: This is wrong. The constant_value field should
+ * FINISHME: not be modified! Imagine a case where a shader
+ * FINISHME: without an initializer is linked in two different
+ * FINISHME: programs with shaders that have differing
+ * FINISHME: initializers. Linking with the first will
+ * FINISHME: modify the shader, and linking with the second
+ * FINISHME: will fail.
+ */
existing->constant_value =
- (ir_constant *)var->constant_value->clone(NULL);
+ var->constant_value->clone(talloc_parent(existing), NULL);
}
} else
variables.add_variable(var->name, var);
*/
if (input->type != output->type) {
linker_error_printf(prog,
- "%s shader output `%s' delcared as "
+ "%s shader output `%s' declared as "
"type `%s', but %s shader input declared "
"as type `%s'\n",
producer_stage, output->name,
* should be added.
*/
void
-remap_variables(ir_instruction *inst, glsl_symbol_table *symbols,
- exec_list *instructions)
+remap_variables(ir_instruction *inst, struct gl_shader *target,
+ hash_table *temps)
{
class remap_visitor : public ir_hierarchical_visitor {
public:
- remap_visitor(glsl_symbol_table *symbols, exec_list *instructions)
+ remap_visitor(struct gl_shader *target,
+ hash_table *temps)
{
- this->symbols = symbols;
- this->instructions = instructions;
+ 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->mode == ir_var_temporary) {
+ ir_variable *var = (ir_variable *) hash_table_find(temps, ir->var);
+
+ 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(NULL);
+ ir_variable *copy = ir->var->clone(this->target, NULL);
this->symbols->add_variable(copy->name, copy);
this->instructions->push_head(copy);
+ ir->var = copy;
}
return visit_continue;
}
private:
+ struct gl_shader *target;
glsl_symbol_table *symbols;
exec_list *instructions;
+ hash_table *temps;
};
- remap_visitor v(symbols, instructions);
+ remap_visitor v(target, temps);
inst->accept(&v);
}
* Move non-declarations from one instruction stream to another
*
* The intended usage pattern of this function is to pass the pointer to the
- * head sentinal of a list (i.e., a pointer to the list cast to an \c exec_node
+ * head sentinel of a list (i.e., a pointer to the list cast to an \c exec_node
* pointer) for \c last and \c false for \c make_copies on the first
* call. Successive calls pass the return value of the previous call for
* \c last and \c true for \c make_copies.
move_non_declarations(exec_list *instructions, exec_node *last,
bool make_copies, gl_shader *target)
{
- foreach_list(node, instructions) {
+ hash_table *temps = NULL;
+
+ if (make_copies)
+ temps = hash_table_ctor(0, hash_table_pointer_hash,
+ hash_table_pointer_compare);
+
+ foreach_list_safe(node, instructions) {
ir_instruction *inst = (ir_instruction *) node;
- if (inst->as_variable() || inst->as_function())
+ if (inst->as_function())
+ continue;
+
+ ir_variable *var = inst->as_variable();
+ if ((var != NULL) && (var->mode != ir_var_temporary))
continue;
- assert(inst->as_assignment());
+ assert(inst->as_assignment()
+ || ((var != NULL) && (var->mode == ir_var_temporary)));
if (make_copies) {
- inst = inst->clone(NULL);
- remap_variables(inst, target->symbols, target->ir);
+ inst = inst->clone(target, NULL);
+
+ if (var != NULL)
+ hash_table_insert(temps, inst, var);
+ else
+ remap_variables(inst, target, temps);
} else {
inst->remove();
}
last = inst;
}
+ if (make_copies)
+ hash_table_dtor(temps);
+
return last;
}
* shader is returned.
*/
static struct gl_shader *
-link_intrastage_shaders(struct gl_shader_program *prog,
+link_intrastage_shaders(GLcontext *ctx,
+ struct gl_shader_program *prog,
struct gl_shader **shader_list,
unsigned num_shaders)
{
ir_function_signature *sig =
(ir_function_signature *) iter.get();
- if (!sig->is_defined || sig->is_built_in)
+ if (!sig->is_defined || sig->is_builtin)
continue;
ir_function_signature *other_sig =
other->exact_matching_signature(& sig->parameters);
if ((other_sig != NULL) && other_sig->is_defined
- && !other_sig->is_built_in) {
+ && !other_sig->is_builtin) {
linker_error_printf(prog,
"function `%s' is multiply defined",
f->name);
return NULL;
}
- gl_shader *const linked = _mesa_new_shader(NULL, 0, main->Type);
+ gl_shader *const linked = ctx->Driver.NewShader(NULL, 0, main->Type);
linked->ir = new(linked) exec_list;
- clone_ir_list(linked->ir, main->ir);
+ clone_ir_list(linked, linked->ir, main->ir);
populate_symbol_table(linked);
/* Move any instructions other than variable declarations or function
* declarations into main.
*/
- exec_node *insertion_point = (exec_node *) &main_sig->body;
+ 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,
- (shader_list[i] != main),
- linked);
+ insertion_point, true, linked);
}
/* Resolve initializers for global variables in the linked shader.
*/
+ unsigned num_linking_shaders = num_shaders;
+ for (unsigned i = 0; i < num_shaders; i++)
+ num_linking_shaders += shader_list[i]->num_builtins_to_link;
+
+ gl_shader **linking_shaders =
+ (gl_shader **) calloc(num_linking_shaders, sizeof(gl_shader *));
+
+ memcpy(linking_shaders, shader_list,
+ sizeof(linking_shaders[0]) * num_shaders);
+
+ unsigned idx = num_shaders;
+ for (unsigned i = 0; i < num_shaders; i++) {
+ memcpy(&linking_shaders[idx], shader_list[i]->builtins_to_link,
+ sizeof(linking_shaders[0]) * shader_list[i]->num_builtins_to_link);
+ idx += shader_list[i]->num_builtins_to_link;
+ }
+
+ assert(idx == num_linking_shaders);
+
+ link_function_calls(prog, linked, linking_shaders, num_linking_shaders);
+
+ free(linking_shaders);
return linked;
}
unsigned slots;
};
+/**
+ * Update the sizes of linked shader uniform arrays to the maximum
+ * array index used.
+ *
+ * From page 81 (page 95 of the PDF) of the OpenGL 2.1 spec:
+ *
+ * If one or more elements of an array are active,
+ * GetActiveUniform will return the name of the array in name,
+ * subject to the restrictions listed above. The type of the array
+ * is returned in type. The size parameter contains the highest
+ * array element index used, plus one. The compiler or linker
+ * determines the highest index used. There will be only one
+ * active uniform reported by the GL per uniform array.
+
+ */
+static void
+update_array_sizes(struct gl_shader_program *prog)
+{
+ for (unsigned i = 0; i < prog->_NumLinkedShaders; i++) {
+ foreach_list(node, prog->_LinkedShaders[i]->ir) {
+ ir_variable *const var = ((ir_instruction *) node)->as_variable();
+
+ if ((var == NULL) || (var->mode != ir_var_uniform &&
+ var->mode != ir_var_in &&
+ var->mode != ir_var_out) ||
+ !var->type->is_array())
+ continue;
+
+ unsigned int size = var->max_array_access;
+ for (unsigned j = 0; j < prog->_NumLinkedShaders; j++) {
+ foreach_list(node2, prog->_LinkedShaders[j]->ir) {
+ ir_variable *other_var = ((ir_instruction *) node2)->as_variable();
+ if (!other_var)
+ continue;
+
+ if (strcmp(var->name, other_var->name) == 0 &&
+ other_var->max_array_access > size) {
+ size = other_var->max_array_access;
+ }
+ }
+ }
+
+ if (size + 1 != var->type->fields.array->length) {
+ var->type = glsl_type::get_array_instance(var->type->fields.array,
+ size + 1);
+ /* FINISHME: We should update the types of array
+ * dereferences of this variable now.
+ */
+ }
+ }
+ }
+}
+
+static void
+add_uniform(void *mem_ctx, exec_list *uniforms, struct hash_table *ht,
+ const char *name, const glsl_type *type, GLenum shader_type,
+ unsigned *next_shader_pos, unsigned *total_uniforms)
+{
+ if (type->is_record()) {
+ for (unsigned int i = 0; i < type->length; i++) {
+ const glsl_type *field_type = type->fields.structure[i].type;
+ char *field_name = talloc_asprintf(mem_ctx, "%s.%s", name,
+ type->fields.structure[i].name);
+
+ add_uniform(mem_ctx, uniforms, ht, field_name, field_type,
+ shader_type, next_shader_pos, total_uniforms);
+ }
+ } else {
+ uniform_node *n = (uniform_node *) hash_table_find(ht, name);
+ unsigned int vec4_slots;
+ const glsl_type *array_elem_type = NULL;
+
+ if (type->is_array()) {
+ array_elem_type = type->fields.array;
+ /* Array of structures. */
+ if (array_elem_type->is_record()) {
+ for (unsigned int i = 0; i < type->length; i++) {
+ char *elem_name = talloc_asprintf(mem_ctx, "%s[%d]", name, i);
+ add_uniform(mem_ctx, uniforms, ht, elem_name, array_elem_type,
+ shader_type, next_shader_pos, total_uniforms);
+ }
+ return;
+ }
+ }
+
+ /* Fix the storage size of samplers at 1 vec4 each. Be sure to pad out
+ * vectors to vec4 slots.
+ */
+ if (type->is_array()) {
+ if (array_elem_type->is_sampler())
+ vec4_slots = type->length;
+ else
+ vec4_slots = type->length * array_elem_type->matrix_columns;
+ } else if (type->is_sampler()) {
+ vec4_slots = 1;
+ } else {
+ vec4_slots = type->matrix_columns;
+ }
+
+ if (n == NULL) {
+ n = (uniform_node *) calloc(1, sizeof(struct uniform_node));
+ n->u = (gl_uniform *) calloc(1, sizeof(struct gl_uniform));
+ n->slots = vec4_slots;
+
+ n->u->Name = strdup(name);
+ n->u->Type = type;
+ n->u->VertPos = -1;
+ n->u->FragPos = -1;
+ n->u->GeomPos = -1;
+ (*total_uniforms)++;
+
+ hash_table_insert(ht, n, name);
+ uniforms->push_tail(& n->link);
+ }
+
+ switch (shader_type) {
+ case GL_VERTEX_SHADER:
+ n->u->VertPos = *next_shader_pos;
+ break;
+ case GL_FRAGMENT_SHADER:
+ n->u->FragPos = *next_shader_pos;
+ break;
+ case GL_GEOMETRY_SHADER:
+ n->u->GeomPos = *next_shader_pos;
+ break;
+ }
+
+ (*next_shader_pos) += vec4_slots;
+ }
+}
+
void
assign_uniform_locations(struct gl_shader_program *prog)
{
unsigned total_uniforms = 0;
hash_table *ht = hash_table_ctor(32, hash_table_string_hash,
hash_table_string_compare);
+ void *mem_ctx = talloc_new(NULL);
for (unsigned i = 0; i < prog->_NumLinkedShaders; i++) {
unsigned next_position = 0;
if ((var == NULL) || (var->mode != ir_var_uniform))
continue;
- const unsigned vec4_slots = (var->component_slots() + 3) / 4;
- assert(vec4_slots != 0);
-
- uniform_node *n = (uniform_node *) hash_table_find(ht, var->name);
- if (n == NULL) {
- n = (uniform_node *) calloc(1, sizeof(struct uniform_node));
- n->u = (gl_uniform *) calloc(vec4_slots, sizeof(struct gl_uniform));
- n->slots = vec4_slots;
-
- n->u[0].Name = strdup(var->name);
- for (unsigned j = 1; j < vec4_slots; j++)
- n->u[j].Name = n->u[0].Name;
-
- hash_table_insert(ht, n, n->u[0].Name);
- uniforms.push_tail(& n->link);
- total_uniforms += vec4_slots;
+ if (strncmp(var->name, "gl_", 3) == 0) {
+ /* At the moment, we don't allocate uniform locations for
+ * builtin uniforms. It's permitted by spec, and we'll
+ * likely switch to doing that at some point, but not yet.
+ */
+ continue;
}
- if (var->constant_value != NULL)
- for (unsigned j = 0; j < vec4_slots; j++)
- n->u[j].Initialized = true;
-
var->location = next_position;
-
- for (unsigned j = 0; j < vec4_slots; j++) {
- switch (prog->_LinkedShaders[i]->Type) {
- case GL_VERTEX_SHADER:
- n->u[j].VertPos = next_position;
- break;
- case GL_FRAGMENT_SHADER:
- n->u[j].FragPos = next_position;
- break;
- case GL_GEOMETRY_SHADER:
- /* FINISHME: Support geometry shaders. */
- assert(prog->_LinkedShaders[i]->Type != GL_GEOMETRY_SHADER);
- break;
- }
-
- next_position++;
- }
+ add_uniform(mem_ctx, &uniforms, ht, var->name, var->type,
+ prog->_LinkedShaders[i]->Type,
+ &next_position, &total_uniforms);
}
}
+ talloc_free(mem_ctx);
+
gl_uniform_list *ul = (gl_uniform_list *)
calloc(1, sizeof(gl_uniform_list));
next = (uniform_node *) node->link.next;
node->link.remove();
- memcpy(&ul->Uniforms[idx], node->u, sizeof(gl_uniform) * node->slots);
- idx += node->slots;
+ memcpy(&ul->Uniforms[idx], node->u, sizeof(gl_uniform));
+ idx++;
free(node->u);
free(node);
if ((var == NULL) || (var->mode != ir_var_in))
continue;
+ if (var->explicit_location) {
+ const unsigned slots = count_attribute_slots(var->type);
+ const unsigned use_mask = (1 << slots) - 1;
+ const int attr = var->location - VERT_ATTRIB_GENERIC0;
+
+ if ((var->location >= (int)(max_attribute_index + VERT_ATTRIB_GENERIC0))
+ || (var->location < 0)) {
+ linker_error_printf(prog,
+ "invalid explicit location %d specified for "
+ "`%s'\n",
+ (var->location < 0) ? var->location : attr,
+ var->name);
+ return false;
+ } else if (var->location >= VERT_ATTRIB_GENERIC0) {
+ used_locations |= (use_mask << attr);
+ }
+ }
+
/* The location was explicitly assigned, nothing to do here.
*/
if (var->location != -1)
* be explicitly assigned by via glBindAttribLocation. Mark it as reserved
* to prevent it from being automatically allocated below.
*/
- used_locations |= (1 << 0);
+ 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++) {
/* Mask representing the contiguous slots that will be used by this
}
+/**
+ * Demote shader outputs that are not read to being just plain global variables
+ */
void
-assign_varying_locations(gl_shader *producer, gl_shader *consumer)
+demote_unread_shader_outputs(gl_shader *sh)
+{
+ foreach_list(node, sh->ir) {
+ ir_variable *const var = ((ir_instruction *) node)->as_variable();
+
+ if ((var == NULL) || (var->mode != ir_var_out))
+ continue;
+
+ /* An 'out' variable is only really a shader output if its value is read
+ * by the following stage.
+ */
+ if (var->location == -1) {
+ var->mode = ir_var_auto;
+ }
+ }
+}
+
+
+void
+assign_varying_locations(struct gl_shader_program *prog,
+ gl_shader *producer, gl_shader *consumer)
{
/* FINISHME: Set dynamically when geometry shader support is added. */
unsigned output_index = VERT_RESULT_VAR0;
assert(input_var->location == -1);
- /* FINISHME: Location assignment will need some changes when arrays,
- * FINISHME: matrices, and structures are allowed as shader inputs /
- * FINISHME: outputs.
- */
output_var->location = output_index;
input_var->location = input_index;
- output_index++;
- input_index++;
- }
+ /* FINISHME: Support for "varying" records in GLSL 1.50. */
+ assert(!output_var->type->is_record());
- foreach_list(node, producer->ir) {
- ir_variable *const var = ((ir_instruction *) node)->as_variable();
+ if (output_var->type->is_array()) {
+ const unsigned slots = output_var->type->length
+ * output_var->type->fields.array->matrix_columns;
- if ((var == NULL) || (var->mode != ir_var_out))
- continue;
+ output_index += slots;
+ input_index += slots;
+ } else {
+ const unsigned slots = output_var->type->matrix_columns;
- /* An 'out' variable is only really a shader output if its value is read
- * by the following stage.
- */
- var->shader_out = (var->location != -1);
+ output_index += slots;
+ input_index += slots;
+ }
}
+ demote_unread_shader_outputs(producer);
+
foreach_list(node, consumer->ir) {
ir_variable *const var = ((ir_instruction *) node)->as_variable();
if ((var == NULL) || (var->mode != ir_var_in))
continue;
- /* An 'in' variable is only really a shader input if its value is written
- * by the previous stage.
- */
- var->shader_in = (var->location != -1);
+ if (var->location == -1) {
+ if (prog->Version <= 120) {
+ /* On page 25 (page 31 of the PDF) of the GLSL 1.20 spec:
+ *
+ * Only those varying variables used (i.e. read) in
+ * the fragment shader executable must be written to
+ * by the vertex shader executable; declaring
+ * superfluous varying variables in a vertex shader is
+ * permissible.
+ *
+ * We interpret this text as meaning that the VS must
+ * write the variable for the FS to read it. See
+ * "glsl1-varying read but not written" in piglit.
+ */
+
+ linker_error_printf(prog, "fragment shader varying %s not written "
+ "by vertex shader\n.", var->name);
+ prog->LinkStatus = false;
+ }
+
+ /* An 'in' variable is only really a shader input if its
+ * value is written by the previous stage.
+ */
+ var->mode = ir_var_auto;
+ }
}
}
void
-link_shaders(struct gl_shader_program *prog)
+link_shaders(GLcontext *ctx, struct gl_shader_program *prog)
{
prog->LinkStatus = false;
prog->Validated = false;
calloc(2 * prog->NumShaders, sizeof(struct gl_shader *));
frag_shader_list = &vert_shader_list[prog->NumShaders];
+ 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);
+
switch (prog->Shaders[i]->Type) {
case GL_VERTEX_SHADER:
vert_shader_list[num_vert_shaders] = prog->Shaders[i];
}
}
- /* FINISHME: Implement intra-stage linking. */
+ /* Previous to GLSL version 1.30, different compilation units could mix and
+ * match shading language versions. With GLSL 1.30 and later, the versions
+ * of all shaders must match.
+ */
+ assert(min_version >= 100);
+ assert(max_version <= 130);
+ if ((max_version >= 130 || min_version == 100)
+ && min_version != max_version) {
+ linker_error_printf(prog, "all shaders must use same shading "
+ "language version\n");
+ goto done;
+ }
+
+ prog->Version = max_version;
+
+ for (unsigned int i = 0; i < prog->_NumLinkedShaders; i++) {
+ ctx->Driver.DeleteShader(ctx, prog->_LinkedShaders[i]);
+ }
+
+ /* Link all shaders for a particular stage and validate the result.
+ */
prog->_NumLinkedShaders = 0;
if (num_vert_shaders > 0) {
gl_shader *const sh =
- link_intrastage_shaders(prog, vert_shader_list, num_vert_shaders);
+ link_intrastage_shaders(ctx, prog, vert_shader_list, num_vert_shaders);
if (sh == NULL)
goto done;
if (num_frag_shaders > 0) {
gl_shader *const sh =
- link_intrastage_shaders(prog, frag_shader_list, num_frag_shaders);
+ link_intrastage_shaders(ctx, prog, frag_shader_list, num_frag_shaders);
if (sh == NULL)
goto done;
prog->LinkStatus = true;
}
- /* FINISHME: Perform whole-program optimization here. */
+ /* Do common optimization before assigning storage for attributes,
+ * uniforms, and varyings. Later optimization could possibly make
+ * some of that unused.
+ */
+ for (unsigned i = 0; i < prog->_NumLinkedShaders; i++) {
+ while (do_common_optimization(prog->_LinkedShaders[i]->ir, true, 32))
+ ;
+ }
+
+ update_array_sizes(prog);
assign_uniform_locations(prog);
- if (prog->_LinkedShaders[0]->Type == GL_VERTEX_SHADER)
+ if (prog->_NumLinkedShaders && prog->_LinkedShaders[0]->Type == GL_VERTEX_SHADER) {
/* FINISHME: The value of the max_attribute_index parameter is
* FINISHME: implementation dependent based on the value of
* FINISHME: GL_MAX_VERTEX_ATTRIBS. GL_MAX_VERTEX_ATTRIBS must be
* FINISHME: at least 16, so hardcode 16 for now.
*/
- if (!assign_attribute_locations(prog, 16))
+ if (!assign_attribute_locations(prog, 16)) {
+ prog->LinkStatus = false;
goto done;
+ }
+
+ if (prog->_NumLinkedShaders == 1)
+ demote_unread_shader_outputs(prog->_LinkedShaders[0]);
+ }
for (unsigned i = 1; i < prog->_NumLinkedShaders; i++)
- assign_varying_locations(prog->_LinkedShaders[i - 1],
+ assign_varying_locations(prog,
+ prog->_LinkedShaders[i - 1],
prog->_LinkedShaders[i]);
/* FINISHME: Assign fragment shader output locations. */