aboutsummaryrefslogtreecommitdiff
path: root/mesalib/src
diff options
context:
space:
mode:
authormarha <marha@users.sourceforge.net>2011-02-16 17:16:41 +0000
committermarha <marha@users.sourceforge.net>2011-02-16 17:16:41 +0000
commit41723858b0effb450ce946297e5a606bc7348be2 (patch)
tree8831ace435ad11e06f196e62f0fb4d63fa740deb /mesalib/src
parent92fef6a3a6851ee123dd793788aac50c0831a964 (diff)
parent48d0dcbd5b7f80810ce259bc9ed6f57f99e27ca9 (diff)
downloadvcxsrv-41723858b0effb450ce946297e5a606bc7348be2.tar.gz
vcxsrv-41723858b0effb450ce946297e5a606bc7348be2.tar.bz2
vcxsrv-41723858b0effb450ce946297e5a606bc7348be2.zip
svn merge ^/branches/released .
Diffstat (limited to 'mesalib/src')
-rw-r--r--mesalib/src/glsl/ir_constant_expression.cpp2748
-rw-r--r--mesalib/src/glsl/linker.cpp3426
-rw-r--r--mesalib/src/mesa/main/mtypes.h5
-rw-r--r--mesalib/src/mesa/main/state.c2
-rw-r--r--mesalib/src/mesa/state_tracker/st_atom_blend.c19
-rw-r--r--mesalib/src/mesa/state_tracker/st_cb_bitmap.c5
-rw-r--r--mesalib/src/mesa/state_tracker/st_cb_bufferobjects.c33
-rw-r--r--mesalib/src/mesa/state_tracker/st_cb_clear.c6
-rw-r--r--mesalib/src/mesa/state_tracker/st_cb_drawpixels.c5
-rw-r--r--mesalib/src/mesa/state_tracker/st_cb_drawtex.c5
-rw-r--r--mesalib/src/mesa/state_tracker/st_context.c5
-rw-r--r--mesalib/src/mesa/state_tracker/st_context.h5
-rw-r--r--mesalib/src/mesa/state_tracker/st_draw.c329
-rw-r--r--mesalib/src/mesa/state_tracker/st_draw_feedback.c1
-rw-r--r--mesalib/src/mesa/state_tracker/st_gen_mipmap.c10
-rw-r--r--mesalib/src/mesa/tnl/t_draw.c40
-rw-r--r--mesalib/src/mesa/vbo/vbo_exec_array.c5
-rw-r--r--mesalib/src/mesa/vbo/vbo_exec_draw.c1
-rw-r--r--mesalib/src/mesa/vbo/vbo_save_draw.c1
19 files changed, 3351 insertions, 3300 deletions
diff --git a/mesalib/src/glsl/ir_constant_expression.cpp b/mesalib/src/glsl/ir_constant_expression.cpp
index 2841fb350..1d39e31fa 100644
--- a/mesalib/src/glsl/ir_constant_expression.cpp
+++ b/mesalib/src/glsl/ir_constant_expression.cpp
@@ -1,1376 +1,1372 @@
-/*
- * Copyright © 2010 Intel Corporation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice (including the next
- * paragraph) shall be included in all copies or substantial portions of the
- * Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
-/**
- * \file ir_constant_expression.cpp
- * Evaluate and process constant valued expressions
- *
- * In GLSL, constant valued expressions are used in several places. These
- * must be processed and evaluated very early in the compilation process.
- *
- * * Sizes of arrays
- * * Initializers for uniforms
- * * Initializers for \c const variables
- */
-
-#include <math.h>
-#include "main/core.h" /* for MAX2, MIN2, CLAMP */
-#include "ir.h"
-#include "ir_visitor.h"
-#include "glsl_types.h"
-
-static float
-dot(ir_constant *op0, ir_constant *op1)
-{
- assert(op0->type->is_float() && op1->type->is_float());
-
- float result = 0;
- for (unsigned c = 0; c < op0->type->components(); c++)
- result += op0->value.f[c] * op1->value.f[c];
-
- return result;
-}
-
-ir_constant *
-ir_expression::constant_expression_value()
-{
- if (this->type->is_error())
- return NULL;
-
- ir_constant *op[Elements(this->operands)] = { NULL, };
- ir_constant_data data;
-
- memset(&data, 0, sizeof(data));
-
- for (unsigned operand = 0; operand < this->get_num_operands(); operand++) {
- op[operand] = this->operands[operand]->constant_expression_value();
- if (!op[operand])
- return NULL;
- }
-
- if (op[1] != NULL)
- assert(op[0]->type->base_type == op[1]->type->base_type);
-
- bool op0_scalar = op[0]->type->is_scalar();
- bool op1_scalar = op[1] != NULL && op[1]->type->is_scalar();
-
- /* When iterating over a vector or matrix's components, we want to increase
- * the loop counter. However, for scalars, we want to stay at 0.
- */
- unsigned c0_inc = op0_scalar ? 0 : 1;
- unsigned c1_inc = op1_scalar ? 0 : 1;
- unsigned components;
- if (op1_scalar || !op[1]) {
- components = op[0]->type->components();
- } else {
- components = op[1]->type->components();
- }
-
- void *ctx = ralloc_parent(this);
-
- /* Handle array operations here, rather than below. */
- if (op[0]->type->is_array()) {
- assert(op[1] != NULL && op[1]->type->is_array());
- switch (this->operation) {
- case ir_binop_all_equal:
- return new(ctx) ir_constant(op[0]->has_value(op[1]));
- case ir_binop_any_nequal:
- return new(ctx) ir_constant(!op[0]->has_value(op[1]));
- default:
- break;
- }
- return NULL;
- }
-
- switch (this->operation) {
- case ir_unop_bit_not:
- switch (op[0]->type->base_type) {
- case GLSL_TYPE_INT:
- for (unsigned c = 0; c < components; c++)
- data.i[c] = ~ op[0]->value.i[c];
- break;
- case GLSL_TYPE_UINT:
- for (unsigned c = 0; c < components; c++)
- data.u[c] = ~ op[0]->value.u[c];
- break;
- default:
- assert(0);
- }
- break;
-
- case ir_unop_logic_not:
- assert(op[0]->type->base_type == GLSL_TYPE_BOOL);
- for (unsigned c = 0; c < op[0]->type->components(); c++)
- data.b[c] = !op[0]->value.b[c];
- break;
-
- case ir_unop_f2i:
- assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- data.i[c] = (int) op[0]->value.f[c];
- }
- break;
- case ir_unop_i2f:
- assert(op[0]->type->base_type == GLSL_TYPE_INT);
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- data.f[c] = (float) op[0]->value.i[c];
- }
- break;
- case ir_unop_u2f:
- assert(op[0]->type->base_type == GLSL_TYPE_UINT);
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- data.f[c] = (float) op[0]->value.u[c];
- }
- break;
- case ir_unop_b2f:
- assert(op[0]->type->base_type == GLSL_TYPE_BOOL);
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- data.f[c] = op[0]->value.b[c] ? 1.0F : 0.0F;
- }
- break;
- case ir_unop_f2b:
- assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- data.b[c] = op[0]->value.f[c] != 0.0F ? true : false;
- }
- break;
- case ir_unop_b2i:
- assert(op[0]->type->base_type == GLSL_TYPE_BOOL);
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- data.u[c] = op[0]->value.b[c] ? 1 : 0;
- }
- break;
- case ir_unop_i2b:
- assert(op[0]->type->is_integer());
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- data.b[c] = op[0]->value.u[c] ? true : false;
- }
- break;
-
- case ir_unop_any:
- assert(op[0]->type->is_boolean());
- data.b[0] = false;
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- if (op[0]->value.b[c])
- data.b[0] = true;
- }
- break;
-
- case ir_unop_trunc:
- assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- data.f[c] = truncf(op[0]->value.f[c]);
- }
- break;
-
- case ir_unop_ceil:
- assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- data.f[c] = ceilf(op[0]->value.f[c]);
- }
- break;
-
- case ir_unop_floor:
- assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- data.f[c] = floorf(op[0]->value.f[c]);
- }
- break;
-
- case ir_unop_fract:
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- switch (this->type->base_type) {
- case GLSL_TYPE_UINT:
- data.u[c] = 0;
- break;
- case GLSL_TYPE_INT:
- data.i[c] = 0;
- break;
- case GLSL_TYPE_FLOAT:
- data.f[c] = op[0]->value.f[c] - floor(op[0]->value.f[c]);
- break;
- default:
- assert(0);
- }
- }
- break;
-
- case ir_unop_sin:
- case ir_unop_sin_reduced:
- assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- data.f[c] = sinf(op[0]->value.f[c]);
- }
- break;
-
- case ir_unop_cos:
- case ir_unop_cos_reduced:
- assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- data.f[c] = cosf(op[0]->value.f[c]);
- }
- break;
-
- case ir_unop_neg:
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- switch (this->type->base_type) {
- case GLSL_TYPE_UINT:
- data.u[c] = -((int) op[0]->value.u[c]);
- break;
- case GLSL_TYPE_INT:
- data.i[c] = -op[0]->value.i[c];
- break;
- case GLSL_TYPE_FLOAT:
- data.f[c] = -op[0]->value.f[c];
- break;
- default:
- assert(0);
- }
- }
- break;
-
- case ir_unop_abs:
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- switch (this->type->base_type) {
- case GLSL_TYPE_UINT:
- data.u[c] = op[0]->value.u[c];
- break;
- case GLSL_TYPE_INT:
- data.i[c] = op[0]->value.i[c];
- if (data.i[c] < 0)
- data.i[c] = -data.i[c];
- break;
- case GLSL_TYPE_FLOAT:
- data.f[c] = fabs(op[0]->value.f[c]);
- break;
- default:
- assert(0);
- }
- }
- break;
-
- case ir_unop_sign:
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- switch (this->type->base_type) {
- case GLSL_TYPE_UINT:
- data.u[c] = op[0]->value.i[c] > 0;
- break;
- case GLSL_TYPE_INT:
- data.i[c] = (op[0]->value.i[c] > 0) - (op[0]->value.i[c] < 0);
- break;
- case GLSL_TYPE_FLOAT:
- data.f[c] = float((op[0]->value.f[c] > 0)-(op[0]->value.f[c] < 0));
- break;
- default:
- assert(0);
- }
- }
- break;
-
- case ir_unop_rcp:
- /* FINISHME: Emit warning when division-by-zero is detected. */
- assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- switch (this->type->base_type) {
- case GLSL_TYPE_UINT:
- if (op[0]->value.u[c] == 0.0)
- return NULL;
- data.u[c] = 1 / op[0]->value.u[c];
- break;
- case GLSL_TYPE_INT:
- if (op[0]->value.i[c] == 0.0)
- return NULL;
- data.i[c] = 1 / op[0]->value.i[c];
- break;
- case GLSL_TYPE_FLOAT:
- if (op[0]->value.f[c] == 0.0)
- return NULL;
- data.f[c] = 1.0F / op[0]->value.f[c];
- break;
- default:
- assert(0);
- }
- }
- break;
-
- case ir_unop_rsq:
- /* FINISHME: Emit warning when division-by-zero is detected. */
- assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- float s = sqrtf(op[0]->value.f[c]);
- if (s == 0)
- return NULL;
- data.f[c] = 1.0F / s;
- }
- break;
-
- case ir_unop_sqrt:
- assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- data.f[c] = sqrtf(op[0]->value.f[c]);
- }
- break;
-
- case ir_unop_exp:
- assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- data.f[c] = expf(op[0]->value.f[c]);
- }
- break;
-
- case ir_unop_exp2:
- assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- data.f[c] = exp2f(op[0]->value.f[c]);
- }
- break;
-
- case ir_unop_log:
- assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- data.f[c] = logf(op[0]->value.f[c]);
- }
- break;
-
- case ir_unop_log2:
- assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- data.f[c] = log2f(op[0]->value.f[c]);
- }
- break;
-
- case ir_unop_dFdx:
- case ir_unop_dFdy:
- assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- data.f[c] = 0.0;
- }
- break;
-
- case ir_binop_pow:
- assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- data.f[c] = powf(op[0]->value.f[c], op[1]->value.f[c]);
- }
- break;
-
- case ir_binop_dot:
- data.f[0] = dot(op[0], op[1]);
- break;
-
- case ir_binop_min:
- assert(op[0]->type == op[1]->type || op0_scalar || op1_scalar);
- for (unsigned c = 0, c0 = 0, c1 = 0;
- c < components;
- c0 += c0_inc, c1 += c1_inc, c++) {
-
- switch (op[0]->type->base_type) {
- case GLSL_TYPE_UINT:
- data.u[c] = MIN2(op[0]->value.u[c0], op[1]->value.u[c1]);
- break;
- case GLSL_TYPE_INT:
- data.i[c] = MIN2(op[0]->value.i[c0], op[1]->value.i[c1]);
- break;
- case GLSL_TYPE_FLOAT:
- data.f[c] = MIN2(op[0]->value.f[c0], op[1]->value.f[c1]);
- break;
- default:
- assert(0);
- }
- }
-
- break;
- case ir_binop_max:
- assert(op[0]->type == op[1]->type || op0_scalar || op1_scalar);
- for (unsigned c = 0, c0 = 0, c1 = 0;
- c < components;
- c0 += c0_inc, c1 += c1_inc, c++) {
-
- switch (op[0]->type->base_type) {
- case GLSL_TYPE_UINT:
- data.u[c] = MAX2(op[0]->value.u[c0], op[1]->value.u[c1]);
- break;
- case GLSL_TYPE_INT:
- data.i[c] = MAX2(op[0]->value.i[c0], op[1]->value.i[c1]);
- break;
- case GLSL_TYPE_FLOAT:
- data.f[c] = MAX2(op[0]->value.f[c0], op[1]->value.f[c1]);
- break;
- default:
- assert(0);
- }
- }
- break;
-
- case ir_binop_add:
- assert(op[0]->type == op[1]->type || op0_scalar || op1_scalar);
- for (unsigned c = 0, c0 = 0, c1 = 0;
- c < components;
- c0 += c0_inc, c1 += c1_inc, c++) {
-
- switch (op[0]->type->base_type) {
- case GLSL_TYPE_UINT:
- data.u[c] = op[0]->value.u[c0] + op[1]->value.u[c1];
- break;
- case GLSL_TYPE_INT:
- data.i[c] = op[0]->value.i[c0] + op[1]->value.i[c1];
- break;
- case GLSL_TYPE_FLOAT:
- data.f[c] = op[0]->value.f[c0] + op[1]->value.f[c1];
- break;
- default:
- assert(0);
- }
- }
-
- break;
- case ir_binop_sub:
- assert(op[0]->type == op[1]->type || op0_scalar || op1_scalar);
- for (unsigned c = 0, c0 = 0, c1 = 0;
- c < components;
- c0 += c0_inc, c1 += c1_inc, c++) {
-
- switch (op[0]->type->base_type) {
- case GLSL_TYPE_UINT:
- data.u[c] = op[0]->value.u[c0] - op[1]->value.u[c1];
- break;
- case GLSL_TYPE_INT:
- data.i[c] = op[0]->value.i[c0] - op[1]->value.i[c1];
- break;
- case GLSL_TYPE_FLOAT:
- data.f[c] = op[0]->value.f[c0] - op[1]->value.f[c1];
- break;
- default:
- assert(0);
- }
- }
-
- break;
- case ir_binop_mul:
- /* Check for equal types, or unequal types involving scalars */
- if ((op[0]->type == op[1]->type && !op[0]->type->is_matrix())
- || op0_scalar || op1_scalar) {
- for (unsigned c = 0, c0 = 0, c1 = 0;
- c < components;
- c0 += c0_inc, c1 += c1_inc, c++) {
-
- switch (op[0]->type->base_type) {
- case GLSL_TYPE_UINT:
- data.u[c] = op[0]->value.u[c0] * op[1]->value.u[c1];
- break;
- case GLSL_TYPE_INT:
- data.i[c] = op[0]->value.i[c0] * op[1]->value.i[c1];
- break;
- case GLSL_TYPE_FLOAT:
- data.f[c] = op[0]->value.f[c0] * op[1]->value.f[c1];
- break;
- default:
- assert(0);
- }
- }
- } else {
- assert(op[0]->type->is_matrix() || op[1]->type->is_matrix());
-
- /* Multiply an N-by-M matrix with an M-by-P matrix. Since either
- * matrix can be a GLSL vector, either N or P can be 1.
- *
- * For vec*mat, the vector is treated as a row vector. This
- * means the vector is a 1-row x M-column matrix.
- *
- * For mat*vec, the vector is treated as a column vector. Since
- * matrix_columns is 1 for vectors, this just works.
- */
- const unsigned n = op[0]->type->is_vector()
- ? 1 : op[0]->type->vector_elements;
- const unsigned m = op[1]->type->vector_elements;
- const unsigned p = op[1]->type->matrix_columns;
- for (unsigned j = 0; j < p; j++) {
- for (unsigned i = 0; i < n; i++) {
- for (unsigned k = 0; k < m; k++) {
- data.f[i+n*j] += op[0]->value.f[i+n*k]*op[1]->value.f[k+m*j];
- }
- }
- }
- }
-
- break;
- case ir_binop_div:
- /* FINISHME: Emit warning when division-by-zero is detected. */
- assert(op[0]->type == op[1]->type || op0_scalar || op1_scalar);
- for (unsigned c = 0, c0 = 0, c1 = 0;
- c < components;
- c0 += c0_inc, c1 += c1_inc, c++) {
-
- switch (op[0]->type->base_type) {
- case GLSL_TYPE_UINT:
- if (op[1]->value.u[c1] == 0)
- return NULL;
- data.u[c] = op[0]->value.u[c0] / op[1]->value.u[c1];
- break;
- case GLSL_TYPE_INT:
- if (op[1]->value.i[c1] == 0)
- return NULL;
- data.i[c] = op[0]->value.i[c0] / op[1]->value.i[c1];
- break;
- case GLSL_TYPE_FLOAT:
- if (op[1]->value.f[c1] == 0)
- return NULL;
- data.f[c] = op[0]->value.f[c0] / op[1]->value.f[c1];
- break;
- default:
- assert(0);
- }
- }
-
- break;
- case ir_binop_mod:
- /* FINISHME: Emit warning when division-by-zero is detected. */
- assert(op[0]->type == op[1]->type || op0_scalar || op1_scalar);
- for (unsigned c = 0, c0 = 0, c1 = 0;
- c < components;
- c0 += c0_inc, c1 += c1_inc, c++) {
-
- switch (op[0]->type->base_type) {
- case GLSL_TYPE_UINT:
- if (op[1]->value.u[c1] == 0)
- return NULL;
- data.u[c] = op[0]->value.u[c0] % op[1]->value.u[c1];
- break;
- case GLSL_TYPE_INT:
- if (op[1]->value.i[c1] == 0)
- return NULL;
- data.i[c] = op[0]->value.i[c0] % op[1]->value.i[c1];
- break;
- case GLSL_TYPE_FLOAT:
- if (op[1]->value.f[c1] == 0)
- return NULL;
- /* We don't use fmod because it rounds toward zero; GLSL specifies
- * the use of floor.
- */
- data.f[c] = op[0]->value.f[c0] - op[1]->value.f[c1]
- * floorf(op[0]->value.f[c0] / op[1]->value.f[c1]);
- break;
- default:
- assert(0);
- }
- }
-
- break;
-
- case ir_binop_logic_and:
- assert(op[0]->type->base_type == GLSL_TYPE_BOOL);
- for (unsigned c = 0; c < op[0]->type->components(); c++)
- data.b[c] = op[0]->value.b[c] && op[1]->value.b[c];
- break;
- case ir_binop_logic_xor:
- assert(op[0]->type->base_type == GLSL_TYPE_BOOL);
- for (unsigned c = 0; c < op[0]->type->components(); c++)
- data.b[c] = op[0]->value.b[c] ^ op[1]->value.b[c];
- break;
- case ir_binop_logic_or:
- assert(op[0]->type->base_type == GLSL_TYPE_BOOL);
- for (unsigned c = 0; c < op[0]->type->components(); c++)
- data.b[c] = op[0]->value.b[c] || op[1]->value.b[c];
- break;
-
- case ir_binop_less:
- assert(op[0]->type == op[1]->type);
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- switch (op[0]->type->base_type) {
- case GLSL_TYPE_UINT:
- data.b[0] = op[0]->value.u[0] < op[1]->value.u[0];
- break;
- case GLSL_TYPE_INT:
- data.b[0] = op[0]->value.i[0] < op[1]->value.i[0];
- break;
- case GLSL_TYPE_FLOAT:
- data.b[0] = op[0]->value.f[0] < op[1]->value.f[0];
- break;
- default:
- assert(0);
- }
- }
- break;
- case ir_binop_greater:
- assert(op[0]->type == op[1]->type);
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- switch (op[0]->type->base_type) {
- case GLSL_TYPE_UINT:
- data.b[c] = op[0]->value.u[c] > op[1]->value.u[c];
- break;
- case GLSL_TYPE_INT:
- data.b[c] = op[0]->value.i[c] > op[1]->value.i[c];
- break;
- case GLSL_TYPE_FLOAT:
- data.b[c] = op[0]->value.f[c] > op[1]->value.f[c];
- break;
- default:
- assert(0);
- }
- }
- break;
- case ir_binop_lequal:
- assert(op[0]->type == op[1]->type);
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- switch (op[0]->type->base_type) {
- case GLSL_TYPE_UINT:
- data.b[0] = op[0]->value.u[0] <= op[1]->value.u[0];
- break;
- case GLSL_TYPE_INT:
- data.b[0] = op[0]->value.i[0] <= op[1]->value.i[0];
- break;
- case GLSL_TYPE_FLOAT:
- data.b[0] = op[0]->value.f[0] <= op[1]->value.f[0];
- break;
- default:
- assert(0);
- }
- }
- break;
- case ir_binop_gequal:
- assert(op[0]->type == op[1]->type);
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- switch (op[0]->type->base_type) {
- case GLSL_TYPE_UINT:
- data.b[0] = op[0]->value.u[0] >= op[1]->value.u[0];
- break;
- case GLSL_TYPE_INT:
- data.b[0] = op[0]->value.i[0] >= op[1]->value.i[0];
- break;
- case GLSL_TYPE_FLOAT:
- data.b[0] = op[0]->value.f[0] >= op[1]->value.f[0];
- break;
- default:
- assert(0);
- }
- }
- break;
- case ir_binop_equal:
- assert(op[0]->type == op[1]->type);
- for (unsigned c = 0; c < components; c++) {
- switch (op[0]->type->base_type) {
- case GLSL_TYPE_UINT:
- data.b[c] = op[0]->value.u[c] == op[1]->value.u[c];
- break;
- case GLSL_TYPE_INT:
- data.b[c] = op[0]->value.i[c] == op[1]->value.i[c];
- break;
- case GLSL_TYPE_FLOAT:
- data.b[c] = op[0]->value.f[c] == op[1]->value.f[c];
- break;
- default:
- assert(0);
- }
- }
- break;
- case ir_binop_nequal:
- assert(op[0]->type != op[1]->type);
- for (unsigned c = 0; c < components; c++) {
- switch (op[0]->type->base_type) {
- case GLSL_TYPE_UINT:
- data.b[c] = op[0]->value.u[c] != op[1]->value.u[c];
- break;
- case GLSL_TYPE_INT:
- data.b[c] = op[0]->value.i[c] != op[1]->value.i[c];
- break;
- case GLSL_TYPE_FLOAT:
- data.b[c] = op[0]->value.f[c] != op[1]->value.f[c];
- break;
- default:
- assert(0);
- }
- }
- break;
- case ir_binop_all_equal:
- data.b[0] = op[0]->has_value(op[1]);
- break;
- case ir_binop_any_nequal:
- data.b[0] = !op[0]->has_value(op[1]);
- break;
-
- case ir_binop_lshift:
- for (unsigned c = 0, c0 = 0, c1 = 0;
- c < components;
- c0 += c0_inc, c1 += c1_inc, c++) {
-
- if (op[0]->type->base_type == GLSL_TYPE_INT &&
- op[1]->type->base_type == GLSL_TYPE_INT) {
- data.i[c] = op[0]->value.i[c0] << op[1]->value.i[c1];
-
- } else if (op[0]->type->base_type == GLSL_TYPE_INT &&
- op[1]->type->base_type == GLSL_TYPE_UINT) {
- data.i[c] = op[0]->value.i[c0] << op[1]->value.u[c1];
-
- } else if (op[0]->type->base_type == GLSL_TYPE_UINT &&
- op[1]->type->base_type == GLSL_TYPE_INT) {
- data.u[c] = op[0]->value.u[c0] << op[1]->value.i[c1];
-
- } else if (op[0]->type->base_type == GLSL_TYPE_UINT &&
- op[1]->type->base_type == GLSL_TYPE_UINT) {
- data.u[c] = op[0]->value.u[c0] << op[1]->value.u[c1];
- }
- }
- break;
-
- case ir_binop_rshift:
- for (unsigned c = 0, c0 = 0, c1 = 0;
- c < components;
- c0 += c0_inc, c1 += c1_inc, c++) {
-
- if (op[0]->type->base_type == GLSL_TYPE_INT &&
- op[1]->type->base_type == GLSL_TYPE_INT) {
- data.i[c] = op[0]->value.i[c0] >> op[1]->value.i[c1];
-
- } else if (op[0]->type->base_type == GLSL_TYPE_INT &&
- op[1]->type->base_type == GLSL_TYPE_UINT) {
- data.i[c] = op[0]->value.i[c0] >> op[1]->value.u[c1];
-
- } else if (op[0]->type->base_type == GLSL_TYPE_UINT &&
- op[1]->type->base_type == GLSL_TYPE_INT) {
- data.u[c] = op[0]->value.u[c0] >> op[1]->value.i[c1];
-
- } else if (op[0]->type->base_type == GLSL_TYPE_UINT &&
- op[1]->type->base_type == GLSL_TYPE_UINT) {
- data.u[c] = op[0]->value.u[c0] >> op[1]->value.u[c1];
- }
- }
- break;
-
- case ir_binop_bit_and:
- for (unsigned c = 0, c0 = 0, c1 = 0;
- c < components;
- c0 += c0_inc, c1 += c1_inc, c++) {
-
- switch (op[0]->type->base_type) {
- case GLSL_TYPE_INT:
- data.i[c] = op[0]->value.i[c0] & op[1]->value.i[c1];
- break;
- case GLSL_TYPE_UINT:
- data.u[c] = op[0]->value.u[c0] & op[1]->value.u[c1];
- break;
- default:
- assert(0);
- }
- }
- break;
-
- case ir_binop_bit_or:
- for (unsigned c = 0, c0 = 0, c1 = 0;
- c < components;
- c0 += c0_inc, c1 += c1_inc, c++) {
-
- switch (op[0]->type->base_type) {
- case GLSL_TYPE_INT:
- data.i[c] = op[0]->value.i[c0] | op[1]->value.i[c1];
- break;
- case GLSL_TYPE_UINT:
- data.u[c] = op[0]->value.u[c0] | op[1]->value.u[c1];
- break;
- default:
- assert(0);
- }
- }
- break;
-
- case ir_binop_bit_xor:
- for (unsigned c = 0, c0 = 0, c1 = 0;
- c < components;
- c0 += c0_inc, c1 += c1_inc, c++) {
-
- switch (op[0]->type->base_type) {
- case GLSL_TYPE_INT:
- data.i[c] = op[0]->value.i[c0] ^ op[1]->value.i[c1];
- break;
- case GLSL_TYPE_UINT:
- data.u[c] = op[0]->value.u[c0] ^ op[1]->value.u[c1];
- break;
- default:
- assert(0);
- }
- }
- break;
-
- case ir_quadop_vector:
- for (unsigned c = 0; c < this->type->vector_elements; c++) {
- switch (this->type->base_type) {
- case GLSL_TYPE_INT:
- data.i[c] = op[c]->value.i[0];
- break;
- case GLSL_TYPE_UINT:
- data.u[c] = op[c]->value.u[0];
- break;
- case GLSL_TYPE_FLOAT:
- data.f[c] = op[c]->value.f[0];
- break;
- default:
- assert(0);
- }
- }
- break;
-
- default:
- /* FINISHME: Should handle all expression types. */
- return NULL;
- }
-
- return new(ctx) ir_constant(this->type, &data);
-}
-
-
-ir_constant *
-ir_texture::constant_expression_value()
-{
- /* texture lookups aren't constant expressions */
- return NULL;
-}
-
-
-ir_constant *
-ir_swizzle::constant_expression_value()
-{
- ir_constant *v = this->val->constant_expression_value();
-
- if (v != NULL) {
- ir_constant_data data = { { 0 } };
-
- const unsigned swiz_idx[4] = {
- this->mask.x, this->mask.y, this->mask.z, this->mask.w
- };
-
- for (unsigned i = 0; i < this->mask.num_components; i++) {
- switch (v->type->base_type) {
- case GLSL_TYPE_UINT:
- case GLSL_TYPE_INT: data.u[i] = v->value.u[swiz_idx[i]]; break;
- case GLSL_TYPE_FLOAT: data.f[i] = v->value.f[swiz_idx[i]]; break;
- case GLSL_TYPE_BOOL: data.b[i] = v->value.b[swiz_idx[i]]; break;
- default: assert(!"Should not get here."); break;
- }
- }
-
- void *ctx = ralloc_parent(this);
- return new(ctx) ir_constant(this->type, &data);
- }
- return NULL;
-}
-
-
-ir_constant *
-ir_dereference_variable::constant_expression_value()
-{
- /* This may occur during compile and var->type is glsl_type::error_type */
- if (!var)
- return NULL;
-
- /* The constant_value of a uniform variable is its initializer,
- * not the lifetime constant value of the uniform.
- */
- if (var->mode == ir_var_uniform)
- return NULL;
-
- if (!var->constant_value)
- return NULL;
-
- return var->constant_value->clone(ralloc_parent(var), NULL);
-}
-
-
-ir_constant *
-ir_dereference_array::constant_expression_value()
-{
- ir_constant *array = this->array->constant_expression_value();
- ir_constant *idx = this->array_index->constant_expression_value();
-
- if ((array != NULL) && (idx != NULL)) {
- void *ctx = ralloc_parent(this);
- if (array->type->is_matrix()) {
- /* Array access of a matrix results in a vector.
- */
- const unsigned column = idx->value.u[0];
-
- const glsl_type *const column_type = array->type->column_type();
-
- /* Offset in the constant matrix to the first element of the column
- * to be extracted.
- */
- const unsigned mat_idx = column * column_type->vector_elements;
-
- ir_constant_data data = { { 0 } };
-
- switch (column_type->base_type) {
- case GLSL_TYPE_UINT:
- case GLSL_TYPE_INT:
- for (unsigned i = 0; i < column_type->vector_elements; i++)
- data.u[i] = array->value.u[mat_idx + i];
-
- break;
-
- case GLSL_TYPE_FLOAT:
- for (unsigned i = 0; i < column_type->vector_elements; i++)
- data.f[i] = array->value.f[mat_idx + i];
-
- break;
-
- default:
- assert(!"Should not get here.");
- break;
- }
-
- return new(ctx) ir_constant(column_type, &data);
- } else if (array->type->is_vector()) {
- const unsigned component = idx->value.u[0];
-
- return new(ctx) ir_constant(array, component);
- } else {
- const unsigned index = idx->value.u[0];
- return array->get_array_element(index)->clone(ctx, NULL);
- }
- }
- return NULL;
-}
-
-
-ir_constant *
-ir_dereference_record::constant_expression_value()
-{
- ir_constant *v = this->record->constant_expression_value();
-
- return (v != NULL) ? v->get_record_field(this->field) : NULL;
-}
-
-
-ir_constant *
-ir_assignment::constant_expression_value()
-{
- /* FINISHME: Handle CEs involving assignment (return RHS) */
- return NULL;
-}
-
-
-ir_constant *
-ir_constant::constant_expression_value()
-{
- return this;
-}
-
-
-ir_constant *
-ir_call::constant_expression_value()
-{
- if (this->type == glsl_type::error_type)
- return NULL;
-
- /* From the GLSL 1.20 spec, page 23:
- * "Function calls to user-defined functions (non-built-in functions)
- * cannot be used to form constant expressions."
- */
- if (!this->callee->is_builtin)
- return NULL;
-
- unsigned num_parameters = 0;
-
- /* Check if all parameters are constant */
- ir_constant *op[3];
- foreach_list(n, &this->actual_parameters) {
- ir_constant *constant = ((ir_rvalue *) n)->constant_expression_value();
- if (constant == NULL)
- return NULL;
-
- op[num_parameters] = constant;
-
- assert(num_parameters < 3);
- num_parameters++;
- }
-
- /* Individual cases below can either:
- * - Assign "expr" a new ir_expression to evaluate (for basic opcodes)
- * - Fill "data" with appopriate constant data
- * - Return an ir_constant directly.
- */
- void *mem_ctx = ralloc_parent(this);
- ir_expression *expr = NULL;
-
- ir_constant_data data;
- memset(&data, 0, sizeof(data));
-
- const char *callee = this->callee_name();
- if (strcmp(callee, "abs") == 0) {
- expr = new(mem_ctx) ir_expression(ir_unop_abs, type, op[0], NULL);
- } else if (strcmp(callee, "all") == 0) {
- assert(op[0]->type->is_boolean());
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- if (!op[0]->value.b[c])
- return new(mem_ctx) ir_constant(false);
- }
- return new(mem_ctx) ir_constant(true);
- } else if (strcmp(callee, "any") == 0) {
- assert(op[0]->type->is_boolean());
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- if (op[0]->value.b[c])
- return new(mem_ctx) ir_constant(true);
- }
- return new(mem_ctx) ir_constant(false);
- } else if (strcmp(callee, "acos") == 0) {
- assert(op[0]->type->is_float());
- for (unsigned c = 0; c < op[0]->type->components(); c++)
- data.f[c] = acosf(op[0]->value.f[c]);
- } else if (strcmp(callee, "acosh") == 0) {
- assert(op[0]->type->is_float());
- for (unsigned c = 0; c < op[0]->type->components(); c++)
- data.f[c] = acoshf(op[0]->value.f[c]);
- } else if (strcmp(callee, "asin") == 0) {
- assert(op[0]->type->is_float());
- for (unsigned c = 0; c < op[0]->type->components(); c++)
- data.f[c] = asinf(op[0]->value.f[c]);
- } else if (strcmp(callee, "asinh") == 0) {
- assert(op[0]->type->is_float());
- for (unsigned c = 0; c < op[0]->type->components(); c++)
- data.f[c] = asinhf(op[0]->value.f[c]);
- } else if (strcmp(callee, "atan") == 0) {
- assert(op[0]->type->is_float());
- if (num_parameters == 2) {
- assert(op[1]->type->is_float());
- for (unsigned c = 0; c < op[0]->type->components(); c++)
- data.f[c] = atan2f(op[0]->value.f[c], op[1]->value.f[c]);
- } else {
- for (unsigned c = 0; c < op[0]->type->components(); c++)
- data.f[c] = atanf(op[0]->value.f[c]);
- }
- } else if (strcmp(callee, "atanh") == 0) {
- assert(op[0]->type->is_float());
- for (unsigned c = 0; c < op[0]->type->components(); c++)
- data.f[c] = atanhf(op[0]->value.f[c]);
- } else if (strcmp(callee, "dFdx") == 0 || strcmp(callee, "dFdy") == 0) {
- return ir_constant::zero(mem_ctx, this->type);
- } else if (strcmp(callee, "ceil") == 0) {
- expr = new(mem_ctx) ir_expression(ir_unop_ceil, type, op[0], NULL);
- } else if (strcmp(callee, "clamp") == 0) {
- assert(num_parameters == 3);
- unsigned c1_inc = op[1]->type->is_scalar() ? 0 : 1;
- unsigned c2_inc = op[2]->type->is_scalar() ? 0 : 1;
- for (unsigned c = 0, c1 = 0, c2 = 0;
- c < op[0]->type->components();
- c1 += c1_inc, c2 += c2_inc, c++) {
-
- switch (op[0]->type->base_type) {
- case GLSL_TYPE_UINT:
- data.u[c] = CLAMP(op[0]->value.u[c], op[1]->value.u[c1],
- op[2]->value.u[c2]);
- break;
- case GLSL_TYPE_INT:
- data.i[c] = CLAMP(op[0]->value.i[c], op[1]->value.i[c1],
- op[2]->value.i[c2]);
- break;
- case GLSL_TYPE_FLOAT:
- data.f[c] = CLAMP(op[0]->value.f[c], op[1]->value.f[c1],
- op[2]->value.f[c2]);
- break;
- default:
- assert(!"Should not get here.");
- }
- }
- } else if (strcmp(callee, "cos") == 0) {
- expr = new(mem_ctx) ir_expression(ir_unop_cos, type, op[0], NULL);
- } else if (strcmp(callee, "cosh") == 0) {
- assert(op[0]->type->is_float());
- for (unsigned c = 0; c < op[0]->type->components(); c++)
- data.f[c] = coshf(op[0]->value.f[c]);
- } else if (strcmp(callee, "cross") == 0) {
- assert(op[0]->type == glsl_type::vec3_type);
- assert(op[1]->type == glsl_type::vec3_type);
- data.f[0] = (op[0]->value.f[1] * op[1]->value.f[2] -
- op[1]->value.f[1] * op[0]->value.f[2]);
- data.f[1] = (op[0]->value.f[2] * op[1]->value.f[0] -
- op[1]->value.f[2] * op[0]->value.f[0]);
- data.f[2] = (op[0]->value.f[0] * op[1]->value.f[1] -
- op[1]->value.f[0] * op[0]->value.f[1]);
- } else if (strcmp(callee, "degrees") == 0) {
- assert(op[0]->type->is_float());
- for (unsigned c = 0; c < op[0]->type->components(); c++)
- data.f[c] = 180.0F / M_PI * op[0]->value.f[c];
- } else if (strcmp(callee, "distance") == 0) {
- assert(op[0]->type->is_float() && op[1]->type->is_float());
- float length_squared = 0.0;
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- float t = op[0]->value.f[c] - op[1]->value.f[c];
- length_squared += t * t;
- }
- return new(mem_ctx) ir_constant(sqrtf(length_squared));
- } else if (strcmp(callee, "dot") == 0) {
- return new(mem_ctx) ir_constant(dot(op[0], op[1]));
- } else if (strcmp(callee, "equal") == 0) {
- assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector());
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- switch (op[0]->type->base_type) {
- case GLSL_TYPE_UINT:
- data.b[c] = op[0]->value.u[c] == op[1]->value.u[c];
- break;
- case GLSL_TYPE_INT:
- data.b[c] = op[0]->value.i[c] == op[1]->value.i[c];
- break;
- case GLSL_TYPE_FLOAT:
- data.b[c] = op[0]->value.f[c] == op[1]->value.f[c];
- break;
- case GLSL_TYPE_BOOL:
- data.b[c] = op[0]->value.b[c] == op[1]->value.b[c];
- break;
- default:
- assert(!"Should not get here.");
- }
- }
- } else if (strcmp(callee, "exp") == 0) {
- expr = new(mem_ctx) ir_expression(ir_unop_exp, type, op[0], NULL);
- } else if (strcmp(callee, "exp2") == 0) {
- expr = new(mem_ctx) ir_expression(ir_unop_exp2, type, op[0], NULL);
- } else if (strcmp(callee, "faceforward") == 0) {
- if (dot(op[2], op[1]) < 0)
- return op[0];
- for (unsigned c = 0; c < op[0]->type->components(); c++)
- data.f[c] = -op[0]->value.f[c];
- } else if (strcmp(callee, "floor") == 0) {
- expr = new(mem_ctx) ir_expression(ir_unop_floor, type, op[0], NULL);
- } else if (strcmp(callee, "fract") == 0) {
- expr = new(mem_ctx) ir_expression(ir_unop_fract, type, op[0], NULL);
- } else if (strcmp(callee, "fwidth") == 0) {
- return ir_constant::zero(mem_ctx, this->type);
- } else if (strcmp(callee, "greaterThan") == 0) {
- assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector());
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- switch (op[0]->type->base_type) {
- case GLSL_TYPE_UINT:
- data.b[c] = op[0]->value.u[c] > op[1]->value.u[c];
- break;
- case GLSL_TYPE_INT:
- data.b[c] = op[0]->value.i[c] > op[1]->value.i[c];
- break;
- case GLSL_TYPE_FLOAT:
- data.b[c] = op[0]->value.f[c] > op[1]->value.f[c];
- break;
- default:
- assert(!"Should not get here.");
- }
- }
- } else if (strcmp(callee, "greaterThanEqual") == 0) {
- assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector());
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- switch (op[0]->type->base_type) {
- case GLSL_TYPE_UINT:
- data.b[c] = op[0]->value.u[c] >= op[1]->value.u[c];
- break;
- case GLSL_TYPE_INT:
- data.b[c] = op[0]->value.i[c] >= op[1]->value.i[c];
- break;
- case GLSL_TYPE_FLOAT:
- data.b[c] = op[0]->value.f[c] >= op[1]->value.f[c];
- break;
- default:
- assert(!"Should not get here.");
- }
- }
- } else if (strcmp(callee, "inversesqrt") == 0) {
- expr = new(mem_ctx) ir_expression(ir_unop_rsq, type, op[0], NULL);
- } else if (strcmp(callee, "length") == 0) {
- return new(mem_ctx) ir_constant(sqrtf(dot(op[0], op[0])));
- } else if (strcmp(callee, "lessThan") == 0) {
- assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector());
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- switch (op[0]->type->base_type) {
- case GLSL_TYPE_UINT:
- data.b[c] = op[0]->value.u[c] < op[1]->value.u[c];
- break;
- case GLSL_TYPE_INT:
- data.b[c] = op[0]->value.i[c] < op[1]->value.i[c];
- break;
- case GLSL_TYPE_FLOAT:
- data.b[c] = op[0]->value.f[c] < op[1]->value.f[c];
- break;
- default:
- assert(!"Should not get here.");
- }
- }
- } else if (strcmp(callee, "lessThanEqual") == 0) {
- assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector());
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- switch (op[0]->type->base_type) {
- case GLSL_TYPE_UINT:
- data.b[c] = op[0]->value.u[c] <= op[1]->value.u[c];
- break;
- case GLSL_TYPE_INT:
- data.b[c] = op[0]->value.i[c] <= op[1]->value.i[c];
- break;
- case GLSL_TYPE_FLOAT:
- data.b[c] = op[0]->value.f[c] <= op[1]->value.f[c];
- break;
- default:
- assert(!"Should not get here.");
- }
- }
- } else if (strcmp(callee, "log") == 0) {
- expr = new(mem_ctx) ir_expression(ir_unop_log, type, op[0], NULL);
- } else if (strcmp(callee, "log2") == 0) {
- expr = new(mem_ctx) ir_expression(ir_unop_log2, type, op[0], NULL);
- } else if (strcmp(callee, "matrixCompMult") == 0) {
- assert(op[0]->type->is_float() && op[1]->type->is_float());
- for (unsigned c = 0; c < op[0]->type->components(); c++)
- data.f[c] = op[0]->value.f[c] * op[1]->value.f[c];
- } else if (strcmp(callee, "max") == 0) {
- expr = new(mem_ctx) ir_expression(ir_binop_max, type, op[0], op[1]);
- } else if (strcmp(callee, "min") == 0) {
- expr = new(mem_ctx) ir_expression(ir_binop_min, type, op[0], op[1]);
- } else if (strcmp(callee, "mix") == 0) {
- assert(op[0]->type->is_float() && op[1]->type->is_float());
- if (op[2]->type->is_float()) {
- unsigned c2_inc = op[2]->type->is_scalar() ? 0 : 1;
- unsigned components = op[0]->type->components();
- for (unsigned c = 0, c2 = 0; c < components; c2 += c2_inc, c++) {
- data.f[c] = op[0]->value.f[c] * (1 - op[2]->value.f[c2]) +
- op[1]->value.f[c] * op[2]->value.f[c2];
- }
- } else {
- assert(op[2]->type->is_boolean());
- for (unsigned c = 0; c < op[0]->type->components(); c++)
- data.f[c] = op[op[2]->value.b[c] ? 1 : 0]->value.f[c];
- }
- } else if (strcmp(callee, "mod") == 0) {
- expr = new(mem_ctx) ir_expression(ir_binop_mod, type, op[0], op[1]);
- } else if (strcmp(callee, "normalize") == 0) {
- assert(op[0]->type->is_float());
- float length = sqrtf(dot(op[0], op[0]));
-
- if (length == 0)
- return ir_constant::zero(mem_ctx, this->type);
-
- for (unsigned c = 0; c < op[0]->type->components(); c++)
- data.f[c] = op[0]->value.f[c] / length;
- } else if (strcmp(callee, "not") == 0) {
- expr = new(mem_ctx) ir_expression(ir_unop_logic_not, type, op[0], NULL);
- } else if (strcmp(callee, "notEqual") == 0) {
- assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector());
- for (unsigned c = 0; c < op[0]->type->components(); c++) {
- switch (op[0]->type->base_type) {
- case GLSL_TYPE_UINT:
- data.b[c] = op[0]->value.u[c] != op[1]->value.u[c];
- break;
- case GLSL_TYPE_INT:
- data.b[c] = op[0]->value.i[c] != op[1]->value.i[c];
- break;
- case GLSL_TYPE_FLOAT:
- data.b[c] = op[0]->value.f[c] != op[1]->value.f[c];
- break;
- case GLSL_TYPE_BOOL:
- data.b[c] = op[0]->value.b[c] != op[1]->value.b[c];
- break;
- default:
- assert(!"Should not get here.");
- }
- }
- } else if (strcmp(callee, "outerProduct") == 0) {
- assert(op[0]->type->is_vector() && op[1]->type->is_vector());
- const unsigned m = op[0]->type->vector_elements;
- const unsigned n = op[1]->type->vector_elements;
- for (unsigned j = 0; j < n; j++) {
- for (unsigned i = 0; i < m; i++) {
- data.f[i+m*j] = op[0]->value.f[i] * op[1]->value.f[j];
- }
- }
- } else if (strcmp(callee, "pow") == 0) {
- expr = new(mem_ctx) ir_expression(ir_binop_pow, type, op[0], op[1]);
- } else if (strcmp(callee, "radians") == 0) {
- assert(op[0]->type->is_float());
- for (unsigned c = 0; c < op[0]->type->components(); c++)
- data.f[c] = M_PI / 180.0F * op[0]->value.f[c];
- } else if (strcmp(callee, "reflect") == 0) {
- assert(op[0]->type->is_float());
- float dot_NI = dot(op[1], op[0]);
- for (unsigned c = 0; c < op[0]->type->components(); c++)
- data.f[c] = op[0]->value.f[c] - 2 * dot_NI * op[1]->value.f[c];
- } else if (strcmp(callee, "refract") == 0) {
- const float eta = op[2]->value.f[0];
- const float dot_NI = dot(op[1], op[0]);
- const float k = 1.0F - eta * eta * (1.0F - dot_NI * dot_NI);
- if (k < 0.0) {
- return ir_constant::zero(mem_ctx, this->type);
- } else {
- for (unsigned c = 0; c < type->components(); c++) {
- data.f[c] = eta * op[0]->value.f[c] - (eta * dot_NI + sqrtf(k))
- * op[1]->value.f[c];
- }
- }
- } else if (strcmp(callee, "sign") == 0) {
- expr = new(mem_ctx) ir_expression(ir_unop_sign, type, op[0], NULL);
- } else if (strcmp(callee, "sin") == 0) {
- expr = new(mem_ctx) ir_expression(ir_unop_sin, type, op[0], NULL);
- } else if (strcmp(callee, "sinh") == 0) {
- assert(op[0]->type->is_float());
- for (unsigned c = 0; c < op[0]->type->components(); c++)
- data.f[c] = sinhf(op[0]->value.f[c]);
- } else if (strcmp(callee, "smoothstep") == 0) {
- assert(num_parameters == 3);
- assert(op[1]->type == op[0]->type);
- unsigned edge_inc = op[0]->type->is_scalar() ? 0 : 1;
- for (unsigned c = 0, e = 0; c < type->components(); e += edge_inc, c++) {
- const float edge0 = op[0]->value.f[e];
- const float edge1 = op[1]->value.f[e];
- if (edge0 == edge1) {
- data.f[c] = 0.0; /* Avoid a crash - results are undefined anyway */
- } else {
- const float numerator = op[2]->value.f[c] - edge0;
- const float denominator = edge1 - edge0;
- const float t = CLAMP(numerator/denominator, 0, 1);
- data.f[c] = t * t * (3 - 2 * t);
- }
- }
- } else if (strcmp(callee, "sqrt") == 0) {
- expr = new(mem_ctx) ir_expression(ir_unop_sqrt, type, op[0], NULL);
- } else if (strcmp(callee, "step") == 0) {
- assert(op[0]->type->is_float() && op[1]->type->is_float());
- /* op[0] (edge) may be either a scalar or a vector */
- const unsigned c0_inc = op[0]->type->is_scalar() ? 0 : 1;
- for (unsigned c = 0, c0 = 0; c < type->components(); c0 += c0_inc, c++)
- data.f[c] = (op[1]->value.f[c] < op[0]->value.f[c0]) ? 0.0F : 1.0F;
- } else if (strcmp(callee, "tan") == 0) {
- assert(op[0]->type->is_float());
- for (unsigned c = 0; c < op[0]->type->components(); c++)
- data.f[c] = tanf(op[0]->value.f[c]);
- } else if (strcmp(callee, "tanh") == 0) {
- assert(op[0]->type->is_float());
- for (unsigned c = 0; c < op[0]->type->components(); c++)
- data.f[c] = tanhf(op[0]->value.f[c]);
- } else if (strcmp(callee, "transpose") == 0) {
- assert(op[0]->type->is_matrix());
- const unsigned n = op[0]->type->vector_elements;
- const unsigned m = op[0]->type->matrix_columns;
- for (unsigned j = 0; j < m; j++) {
- for (unsigned i = 0; i < n; i++) {
- data.f[m*i+j] += op[0]->value.f[i+n*j];
- }
- }
- } else {
- /* Unsupported builtin - some are not allowed in constant expressions. */
- return NULL;
- }
-
- if (expr != NULL)
- return expr->constant_expression_value();
-
- return new(mem_ctx) ir_constant(this->type, &data);
-}
+/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * \file ir_constant_expression.cpp
+ * Evaluate and process constant valued expressions
+ *
+ * In GLSL, constant valued expressions are used in several places. These
+ * must be processed and evaluated very early in the compilation process.
+ *
+ * * Sizes of arrays
+ * * Initializers for uniforms
+ * * Initializers for \c const variables
+ */
+
+#include <math.h>
+#include "main/core.h" /* for MAX2, MIN2, CLAMP */
+#include "ir.h"
+#include "ir_visitor.h"
+#include "glsl_types.h"
+
+static float
+dot(ir_constant *op0, ir_constant *op1)
+{
+ assert(op0->type->is_float() && op1->type->is_float());
+
+ float result = 0;
+ for (unsigned c = 0; c < op0->type->components(); c++)
+ result += op0->value.f[c] * op1->value.f[c];
+
+ return result;
+}
+
+ir_constant *
+ir_expression::constant_expression_value()
+{
+ if (this->type->is_error())
+ return NULL;
+
+ ir_constant *op[Elements(this->operands)] = { NULL, };
+ ir_constant_data data;
+
+ memset(&data, 0, sizeof(data));
+
+ for (unsigned operand = 0; operand < this->get_num_operands(); operand++) {
+ op[operand] = this->operands[operand]->constant_expression_value();
+ if (!op[operand])
+ return NULL;
+ }
+
+ if (op[1] != NULL)
+ assert(op[0]->type->base_type == op[1]->type->base_type);
+
+ bool op0_scalar = op[0]->type->is_scalar();
+ bool op1_scalar = op[1] != NULL && op[1]->type->is_scalar();
+
+ /* When iterating over a vector or matrix's components, we want to increase
+ * the loop counter. However, for scalars, we want to stay at 0.
+ */
+ unsigned c0_inc = op0_scalar ? 0 : 1;
+ unsigned c1_inc = op1_scalar ? 0 : 1;
+ unsigned components;
+ if (op1_scalar || !op[1]) {
+ components = op[0]->type->components();
+ } else {
+ components = op[1]->type->components();
+ }
+
+ void *ctx = ralloc_parent(this);
+
+ /* Handle array operations here, rather than below. */
+ if (op[0]->type->is_array()) {
+ assert(op[1] != NULL && op[1]->type->is_array());
+ switch (this->operation) {
+ case ir_binop_all_equal:
+ return new(ctx) ir_constant(op[0]->has_value(op[1]));
+ case ir_binop_any_nequal:
+ return new(ctx) ir_constant(!op[0]->has_value(op[1]));
+ default:
+ break;
+ }
+ return NULL;
+ }
+
+ switch (this->operation) {
+ case ir_unop_bit_not:
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_INT:
+ for (unsigned c = 0; c < components; c++)
+ data.i[c] = ~ op[0]->value.i[c];
+ break;
+ case GLSL_TYPE_UINT:
+ for (unsigned c = 0; c < components; c++)
+ data.u[c] = ~ op[0]->value.u[c];
+ break;
+ default:
+ assert(0);
+ }
+ break;
+
+ case ir_unop_logic_not:
+ assert(op[0]->type->base_type == GLSL_TYPE_BOOL);
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.b[c] = !op[0]->value.b[c];
+ break;
+
+ case ir_unop_f2i:
+ assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.i[c] = (int) op[0]->value.f[c];
+ }
+ break;
+ case ir_unop_i2f:
+ assert(op[0]->type->base_type == GLSL_TYPE_INT);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.f[c] = (float) op[0]->value.i[c];
+ }
+ break;
+ case ir_unop_u2f:
+ assert(op[0]->type->base_type == GLSL_TYPE_UINT);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.f[c] = (float) op[0]->value.u[c];
+ }
+ break;
+ case ir_unop_b2f:
+ assert(op[0]->type->base_type == GLSL_TYPE_BOOL);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.f[c] = op[0]->value.b[c] ? 1.0F : 0.0F;
+ }
+ break;
+ case ir_unop_f2b:
+ assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.b[c] = op[0]->value.f[c] != 0.0F ? true : false;
+ }
+ break;
+ case ir_unop_b2i:
+ assert(op[0]->type->base_type == GLSL_TYPE_BOOL);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.u[c] = op[0]->value.b[c] ? 1 : 0;
+ }
+ break;
+ case ir_unop_i2b:
+ assert(op[0]->type->is_integer());
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.b[c] = op[0]->value.u[c] ? true : false;
+ }
+ break;
+
+ case ir_unop_any:
+ assert(op[0]->type->is_boolean());
+ data.b[0] = false;
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ if (op[0]->value.b[c])
+ data.b[0] = true;
+ }
+ break;
+
+ case ir_unop_trunc:
+ assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.f[c] = truncf(op[0]->value.f[c]);
+ }
+ break;
+
+ case ir_unop_ceil:
+ assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.f[c] = ceilf(op[0]->value.f[c]);
+ }
+ break;
+
+ case ir_unop_floor:
+ assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.f[c] = floorf(op[0]->value.f[c]);
+ }
+ break;
+
+ case ir_unop_fract:
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ switch (this->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.u[c] = 0;
+ break;
+ case GLSL_TYPE_INT:
+ data.i[c] = 0;
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.f[c] = op[0]->value.f[c] - floor(op[0]->value.f[c]);
+ break;
+ default:
+ assert(0);
+ }
+ }
+ break;
+
+ case ir_unop_sin:
+ case ir_unop_sin_reduced:
+ assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.f[c] = sinf(op[0]->value.f[c]);
+ }
+ break;
+
+ case ir_unop_cos:
+ case ir_unop_cos_reduced:
+ assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.f[c] = cosf(op[0]->value.f[c]);
+ }
+ break;
+
+ case ir_unop_neg:
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ switch (this->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.u[c] = -((int) op[0]->value.u[c]);
+ break;
+ case GLSL_TYPE_INT:
+ data.i[c] = -op[0]->value.i[c];
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.f[c] = -op[0]->value.f[c];
+ break;
+ default:
+ assert(0);
+ }
+ }
+ break;
+
+ case ir_unop_abs:
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ switch (this->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.u[c] = op[0]->value.u[c];
+ break;
+ case GLSL_TYPE_INT:
+ data.i[c] = op[0]->value.i[c];
+ if (data.i[c] < 0)
+ data.i[c] = -data.i[c];
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.f[c] = fabs(op[0]->value.f[c]);
+ break;
+ default:
+ assert(0);
+ }
+ }
+ break;
+
+ case ir_unop_sign:
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ switch (this->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.u[c] = op[0]->value.i[c] > 0;
+ break;
+ case GLSL_TYPE_INT:
+ data.i[c] = (op[0]->value.i[c] > 0) - (op[0]->value.i[c] < 0);
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.f[c] = float((op[0]->value.f[c] > 0)-(op[0]->value.f[c] < 0));
+ break;
+ default:
+ assert(0);
+ }
+ }
+ break;
+
+ case ir_unop_rcp:
+ assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ switch (this->type->base_type) {
+ case GLSL_TYPE_UINT:
+ if (op[0]->value.u[c] != 0.0)
+ data.u[c] = 1 / op[0]->value.u[c];
+ break;
+ case GLSL_TYPE_INT:
+ if (op[0]->value.i[c] != 0.0)
+ data.i[c] = 1 / op[0]->value.i[c];
+ break;
+ case GLSL_TYPE_FLOAT:
+ if (op[0]->value.f[c] != 0.0)
+ data.f[c] = 1.0F / op[0]->value.f[c];
+ break;
+ default:
+ assert(0);
+ }
+ }
+ break;
+
+ case ir_unop_rsq:
+ assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.f[c] = 1.0F / sqrtf(op[0]->value.f[c]);
+ }
+ break;
+
+ case ir_unop_sqrt:
+ assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.f[c] = sqrtf(op[0]->value.f[c]);
+ }
+ break;
+
+ case ir_unop_exp:
+ assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.f[c] = expf(op[0]->value.f[c]);
+ }
+ break;
+
+ case ir_unop_exp2:
+ assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.f[c] = exp2f(op[0]->value.f[c]);
+ }
+ break;
+
+ case ir_unop_log:
+ assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.f[c] = logf(op[0]->value.f[c]);
+ }
+ break;
+
+ case ir_unop_log2:
+ assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.f[c] = log2f(op[0]->value.f[c]);
+ }
+ break;
+
+ case ir_unop_dFdx:
+ case ir_unop_dFdy:
+ assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.f[c] = 0.0;
+ }
+ break;
+
+ case ir_binop_pow:
+ assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.f[c] = powf(op[0]->value.f[c], op[1]->value.f[c]);
+ }
+ break;
+
+ case ir_binop_dot:
+ data.f[0] = dot(op[0], op[1]);
+ break;
+
+ case ir_binop_min:
+ assert(op[0]->type == op[1]->type || op0_scalar || op1_scalar);
+ for (unsigned c = 0, c0 = 0, c1 = 0;
+ c < components;
+ c0 += c0_inc, c1 += c1_inc, c++) {
+
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.u[c] = MIN2(op[0]->value.u[c0], op[1]->value.u[c1]);
+ break;
+ case GLSL_TYPE_INT:
+ data.i[c] = MIN2(op[0]->value.i[c0], op[1]->value.i[c1]);
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.f[c] = MIN2(op[0]->value.f[c0], op[1]->value.f[c1]);
+ break;
+ default:
+ assert(0);
+ }
+ }
+
+ break;
+ case ir_binop_max:
+ assert(op[0]->type == op[1]->type || op0_scalar || op1_scalar);
+ for (unsigned c = 0, c0 = 0, c1 = 0;
+ c < components;
+ c0 += c0_inc, c1 += c1_inc, c++) {
+
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.u[c] = MAX2(op[0]->value.u[c0], op[1]->value.u[c1]);
+ break;
+ case GLSL_TYPE_INT:
+ data.i[c] = MAX2(op[0]->value.i[c0], op[1]->value.i[c1]);
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.f[c] = MAX2(op[0]->value.f[c0], op[1]->value.f[c1]);
+ break;
+ default:
+ assert(0);
+ }
+ }
+ break;
+
+ case ir_binop_add:
+ assert(op[0]->type == op[1]->type || op0_scalar || op1_scalar);
+ for (unsigned c = 0, c0 = 0, c1 = 0;
+ c < components;
+ c0 += c0_inc, c1 += c1_inc, c++) {
+
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.u[c] = op[0]->value.u[c0] + op[1]->value.u[c1];
+ break;
+ case GLSL_TYPE_INT:
+ data.i[c] = op[0]->value.i[c0] + op[1]->value.i[c1];
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.f[c] = op[0]->value.f[c0] + op[1]->value.f[c1];
+ break;
+ default:
+ assert(0);
+ }
+ }
+
+ break;
+ case ir_binop_sub:
+ assert(op[0]->type == op[1]->type || op0_scalar || op1_scalar);
+ for (unsigned c = 0, c0 = 0, c1 = 0;
+ c < components;
+ c0 += c0_inc, c1 += c1_inc, c++) {
+
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.u[c] = op[0]->value.u[c0] - op[1]->value.u[c1];
+ break;
+ case GLSL_TYPE_INT:
+ data.i[c] = op[0]->value.i[c0] - op[1]->value.i[c1];
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.f[c] = op[0]->value.f[c0] - op[1]->value.f[c1];
+ break;
+ default:
+ assert(0);
+ }
+ }
+
+ break;
+ case ir_binop_mul:
+ /* Check for equal types, or unequal types involving scalars */
+ if ((op[0]->type == op[1]->type && !op[0]->type->is_matrix())
+ || op0_scalar || op1_scalar) {
+ for (unsigned c = 0, c0 = 0, c1 = 0;
+ c < components;
+ c0 += c0_inc, c1 += c1_inc, c++) {
+
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.u[c] = op[0]->value.u[c0] * op[1]->value.u[c1];
+ break;
+ case GLSL_TYPE_INT:
+ data.i[c] = op[0]->value.i[c0] * op[1]->value.i[c1];
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.f[c] = op[0]->value.f[c0] * op[1]->value.f[c1];
+ break;
+ default:
+ assert(0);
+ }
+ }
+ } else {
+ assert(op[0]->type->is_matrix() || op[1]->type->is_matrix());
+
+ /* Multiply an N-by-M matrix with an M-by-P matrix. Since either
+ * matrix can be a GLSL vector, either N or P can be 1.
+ *
+ * For vec*mat, the vector is treated as a row vector. This
+ * means the vector is a 1-row x M-column matrix.
+ *
+ * For mat*vec, the vector is treated as a column vector. Since
+ * matrix_columns is 1 for vectors, this just works.
+ */
+ const unsigned n = op[0]->type->is_vector()
+ ? 1 : op[0]->type->vector_elements;
+ const unsigned m = op[1]->type->vector_elements;
+ const unsigned p = op[1]->type->matrix_columns;
+ for (unsigned j = 0; j < p; j++) {
+ for (unsigned i = 0; i < n; i++) {
+ for (unsigned k = 0; k < m; k++) {
+ data.f[i+n*j] += op[0]->value.f[i+n*k]*op[1]->value.f[k+m*j];
+ }
+ }
+ }
+ }
+
+ break;
+ case ir_binop_div:
+ /* FINISHME: Emit warning when division-by-zero is detected. */
+ assert(op[0]->type == op[1]->type || op0_scalar || op1_scalar);
+ for (unsigned c = 0, c0 = 0, c1 = 0;
+ c < components;
+ c0 += c0_inc, c1 += c1_inc, c++) {
+
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ if (op[1]->value.u[c1] == 0) {
+ data.u[c] = 0;
+ } else {
+ data.u[c] = op[0]->value.u[c0] / op[1]->value.u[c1];
+ }
+ break;
+ case GLSL_TYPE_INT:
+ if (op[1]->value.i[c1] == 0) {
+ data.i[c] = 0;
+ } else {
+ data.i[c] = op[0]->value.i[c0] / op[1]->value.i[c1];
+ }
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.f[c] = op[0]->value.f[c0] / op[1]->value.f[c1];
+ break;
+ default:
+ assert(0);
+ }
+ }
+
+ break;
+ case ir_binop_mod:
+ /* FINISHME: Emit warning when division-by-zero is detected. */
+ assert(op[0]->type == op[1]->type || op0_scalar || op1_scalar);
+ for (unsigned c = 0, c0 = 0, c1 = 0;
+ c < components;
+ c0 += c0_inc, c1 += c1_inc, c++) {
+
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ if (op[1]->value.u[c1] == 0) {
+ data.u[c] = 0;
+ } else {
+ data.u[c] = op[0]->value.u[c0] % op[1]->value.u[c1];
+ }
+ break;
+ case GLSL_TYPE_INT:
+ if (op[1]->value.i[c1] == 0) {
+ data.i[c] = 0;
+ } else {
+ data.i[c] = op[0]->value.i[c0] % op[1]->value.i[c1];
+ }
+ break;
+ case GLSL_TYPE_FLOAT:
+ /* We don't use fmod because it rounds toward zero; GLSL specifies
+ * the use of floor.
+ */
+ data.f[c] = op[0]->value.f[c0] - op[1]->value.f[c1]
+ * floorf(op[0]->value.f[c0] / op[1]->value.f[c1]);
+ break;
+ default:
+ assert(0);
+ }
+ }
+
+ break;
+
+ case ir_binop_logic_and:
+ assert(op[0]->type->base_type == GLSL_TYPE_BOOL);
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.b[c] = op[0]->value.b[c] && op[1]->value.b[c];
+ break;
+ case ir_binop_logic_xor:
+ assert(op[0]->type->base_type == GLSL_TYPE_BOOL);
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.b[c] = op[0]->value.b[c] ^ op[1]->value.b[c];
+ break;
+ case ir_binop_logic_or:
+ assert(op[0]->type->base_type == GLSL_TYPE_BOOL);
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.b[c] = op[0]->value.b[c] || op[1]->value.b[c];
+ break;
+
+ case ir_binop_less:
+ assert(op[0]->type == op[1]->type);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.b[0] = op[0]->value.u[0] < op[1]->value.u[0];
+ break;
+ case GLSL_TYPE_INT:
+ data.b[0] = op[0]->value.i[0] < op[1]->value.i[0];
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.b[0] = op[0]->value.f[0] < op[1]->value.f[0];
+ break;
+ default:
+ assert(0);
+ }
+ }
+ break;
+ case ir_binop_greater:
+ assert(op[0]->type == op[1]->type);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.b[c] = op[0]->value.u[c] > op[1]->value.u[c];
+ break;
+ case GLSL_TYPE_INT:
+ data.b[c] = op[0]->value.i[c] > op[1]->value.i[c];
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.b[c] = op[0]->value.f[c] > op[1]->value.f[c];
+ break;
+ default:
+ assert(0);
+ }
+ }
+ break;
+ case ir_binop_lequal:
+ assert(op[0]->type == op[1]->type);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.b[0] = op[0]->value.u[0] <= op[1]->value.u[0];
+ break;
+ case GLSL_TYPE_INT:
+ data.b[0] = op[0]->value.i[0] <= op[1]->value.i[0];
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.b[0] = op[0]->value.f[0] <= op[1]->value.f[0];
+ break;
+ default:
+ assert(0);
+ }
+ }
+ break;
+ case ir_binop_gequal:
+ assert(op[0]->type == op[1]->type);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.b[0] = op[0]->value.u[0] >= op[1]->value.u[0];
+ break;
+ case GLSL_TYPE_INT:
+ data.b[0] = op[0]->value.i[0] >= op[1]->value.i[0];
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.b[0] = op[0]->value.f[0] >= op[1]->value.f[0];
+ break;
+ default:
+ assert(0);
+ }
+ }
+ break;
+ case ir_binop_equal:
+ assert(op[0]->type == op[1]->type);
+ for (unsigned c = 0; c < components; c++) {
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.b[c] = op[0]->value.u[c] == op[1]->value.u[c];
+ break;
+ case GLSL_TYPE_INT:
+ data.b[c] = op[0]->value.i[c] == op[1]->value.i[c];
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.b[c] = op[0]->value.f[c] == op[1]->value.f[c];
+ break;
+ default:
+ assert(0);
+ }
+ }
+ break;
+ case ir_binop_nequal:
+ assert(op[0]->type != op[1]->type);
+ for (unsigned c = 0; c < components; c++) {
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.b[c] = op[0]->value.u[c] != op[1]->value.u[c];
+ break;
+ case GLSL_TYPE_INT:
+ data.b[c] = op[0]->value.i[c] != op[1]->value.i[c];
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.b[c] = op[0]->value.f[c] != op[1]->value.f[c];
+ break;
+ default:
+ assert(0);
+ }
+ }
+ break;
+ case ir_binop_all_equal:
+ data.b[0] = op[0]->has_value(op[1]);
+ break;
+ case ir_binop_any_nequal:
+ data.b[0] = !op[0]->has_value(op[1]);
+ break;
+
+ case ir_binop_lshift:
+ for (unsigned c = 0, c0 = 0, c1 = 0;
+ c < components;
+ c0 += c0_inc, c1 += c1_inc, c++) {
+
+ if (op[0]->type->base_type == GLSL_TYPE_INT &&
+ op[1]->type->base_type == GLSL_TYPE_INT) {
+ data.i[c] = op[0]->value.i[c0] << op[1]->value.i[c1];
+
+ } else if (op[0]->type->base_type == GLSL_TYPE_INT &&
+ op[1]->type->base_type == GLSL_TYPE_UINT) {
+ data.i[c] = op[0]->value.i[c0] << op[1]->value.u[c1];
+
+ } else if (op[0]->type->base_type == GLSL_TYPE_UINT &&
+ op[1]->type->base_type == GLSL_TYPE_INT) {
+ data.u[c] = op[0]->value.u[c0] << op[1]->value.i[c1];
+
+ } else if (op[0]->type->base_type == GLSL_TYPE_UINT &&
+ op[1]->type->base_type == GLSL_TYPE_UINT) {
+ data.u[c] = op[0]->value.u[c0] << op[1]->value.u[c1];
+ }
+ }
+ break;
+
+ case ir_binop_rshift:
+ for (unsigned c = 0, c0 = 0, c1 = 0;
+ c < components;
+ c0 += c0_inc, c1 += c1_inc, c++) {
+
+ if (op[0]->type->base_type == GLSL_TYPE_INT &&
+ op[1]->type->base_type == GLSL_TYPE_INT) {
+ data.i[c] = op[0]->value.i[c0] >> op[1]->value.i[c1];
+
+ } else if (op[0]->type->base_type == GLSL_TYPE_INT &&
+ op[1]->type->base_type == GLSL_TYPE_UINT) {
+ data.i[c] = op[0]->value.i[c0] >> op[1]->value.u[c1];
+
+ } else if (op[0]->type->base_type == GLSL_TYPE_UINT &&
+ op[1]->type->base_type == GLSL_TYPE_INT) {
+ data.u[c] = op[0]->value.u[c0] >> op[1]->value.i[c1];
+
+ } else if (op[0]->type->base_type == GLSL_TYPE_UINT &&
+ op[1]->type->base_type == GLSL_TYPE_UINT) {
+ data.u[c] = op[0]->value.u[c0] >> op[1]->value.u[c1];
+ }
+ }
+ break;
+
+ case ir_binop_bit_and:
+ for (unsigned c = 0, c0 = 0, c1 = 0;
+ c < components;
+ c0 += c0_inc, c1 += c1_inc, c++) {
+
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_INT:
+ data.i[c] = op[0]->value.i[c0] & op[1]->value.i[c1];
+ break;
+ case GLSL_TYPE_UINT:
+ data.u[c] = op[0]->value.u[c0] & op[1]->value.u[c1];
+ break;
+ default:
+ assert(0);
+ }
+ }
+ break;
+
+ case ir_binop_bit_or:
+ for (unsigned c = 0, c0 = 0, c1 = 0;
+ c < components;
+ c0 += c0_inc, c1 += c1_inc, c++) {
+
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_INT:
+ data.i[c] = op[0]->value.i[c0] | op[1]->value.i[c1];
+ break;
+ case GLSL_TYPE_UINT:
+ data.u[c] = op[0]->value.u[c0] | op[1]->value.u[c1];
+ break;
+ default:
+ assert(0);
+ }
+ }
+ break;
+
+ case ir_binop_bit_xor:
+ for (unsigned c = 0, c0 = 0, c1 = 0;
+ c < components;
+ c0 += c0_inc, c1 += c1_inc, c++) {
+
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_INT:
+ data.i[c] = op[0]->value.i[c0] ^ op[1]->value.i[c1];
+ break;
+ case GLSL_TYPE_UINT:
+ data.u[c] = op[0]->value.u[c0] ^ op[1]->value.u[c1];
+ break;
+ default:
+ assert(0);
+ }
+ }
+ break;
+
+ case ir_quadop_vector:
+ for (unsigned c = 0; c < this->type->vector_elements; c++) {
+ switch (this->type->base_type) {
+ case GLSL_TYPE_INT:
+ data.i[c] = op[c]->value.i[0];
+ break;
+ case GLSL_TYPE_UINT:
+ data.u[c] = op[c]->value.u[0];
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.f[c] = op[c]->value.f[0];
+ break;
+ default:
+ assert(0);
+ }
+ }
+ break;
+
+ default:
+ /* FINISHME: Should handle all expression types. */
+ return NULL;
+ }
+
+ return new(ctx) ir_constant(this->type, &data);
+}
+
+
+ir_constant *
+ir_texture::constant_expression_value()
+{
+ /* texture lookups aren't constant expressions */
+ return NULL;
+}
+
+
+ir_constant *
+ir_swizzle::constant_expression_value()
+{
+ ir_constant *v = this->val->constant_expression_value();
+
+ if (v != NULL) {
+ ir_constant_data data = { { 0 } };
+
+ const unsigned swiz_idx[4] = {
+ this->mask.x, this->mask.y, this->mask.z, this->mask.w
+ };
+
+ for (unsigned i = 0; i < this->mask.num_components; i++) {
+ switch (v->type->base_type) {
+ case GLSL_TYPE_UINT:
+ case GLSL_TYPE_INT: data.u[i] = v->value.u[swiz_idx[i]]; break;
+ case GLSL_TYPE_FLOAT: data.f[i] = v->value.f[swiz_idx[i]]; break;
+ case GLSL_TYPE_BOOL: data.b[i] = v->value.b[swiz_idx[i]]; break;
+ default: assert(!"Should not get here."); break;
+ }
+ }
+
+ void *ctx = ralloc_parent(this);
+ return new(ctx) ir_constant(this->type, &data);
+ }
+ return NULL;
+}
+
+
+ir_constant *
+ir_dereference_variable::constant_expression_value()
+{
+ /* This may occur during compile and var->type is glsl_type::error_type */
+ if (!var)
+ return NULL;
+
+ /* The constant_value of a uniform variable is its initializer,
+ * not the lifetime constant value of the uniform.
+ */
+ if (var->mode == ir_var_uniform)
+ return NULL;
+
+ if (!var->constant_value)
+ return NULL;
+
+ return var->constant_value->clone(ralloc_parent(var), NULL);
+}
+
+
+ir_constant *
+ir_dereference_array::constant_expression_value()
+{
+ ir_constant *array = this->array->constant_expression_value();
+ ir_constant *idx = this->array_index->constant_expression_value();
+
+ if ((array != NULL) && (idx != NULL)) {
+ void *ctx = ralloc_parent(this);
+ if (array->type->is_matrix()) {
+ /* Array access of a matrix results in a vector.
+ */
+ const unsigned column = idx->value.u[0];
+
+ const glsl_type *const column_type = array->type->column_type();
+
+ /* Offset in the constant matrix to the first element of the column
+ * to be extracted.
+ */
+ const unsigned mat_idx = column * column_type->vector_elements;
+
+ ir_constant_data data = { { 0 } };
+
+ switch (column_type->base_type) {
+ case GLSL_TYPE_UINT:
+ case GLSL_TYPE_INT:
+ for (unsigned i = 0; i < column_type->vector_elements; i++)
+ data.u[i] = array->value.u[mat_idx + i];
+
+ break;
+
+ case GLSL_TYPE_FLOAT:
+ for (unsigned i = 0; i < column_type->vector_elements; i++)
+ data.f[i] = array->value.f[mat_idx + i];
+
+ break;
+
+ default:
+ assert(!"Should not get here.");
+ break;
+ }
+
+ return new(ctx) ir_constant(column_type, &data);
+ } else if (array->type->is_vector()) {
+ const unsigned component = idx->value.u[0];
+
+ return new(ctx) ir_constant(array, component);
+ } else {
+ const unsigned index = idx->value.u[0];
+ return array->get_array_element(index)->clone(ctx, NULL);
+ }
+ }
+ return NULL;
+}
+
+
+ir_constant *
+ir_dereference_record::constant_expression_value()
+{
+ ir_constant *v = this->record->constant_expression_value();
+
+ return (v != NULL) ? v->get_record_field(this->field) : NULL;
+}
+
+
+ir_constant *
+ir_assignment::constant_expression_value()
+{
+ /* FINISHME: Handle CEs involving assignment (return RHS) */
+ return NULL;
+}
+
+
+ir_constant *
+ir_constant::constant_expression_value()
+{
+ return this;
+}
+
+
+ir_constant *
+ir_call::constant_expression_value()
+{
+ if (this->type == glsl_type::error_type)
+ return NULL;
+
+ /* From the GLSL 1.20 spec, page 23:
+ * "Function calls to user-defined functions (non-built-in functions)
+ * cannot be used to form constant expressions."
+ */
+ if (!this->callee->is_builtin)
+ return NULL;
+
+ unsigned num_parameters = 0;
+
+ /* Check if all parameters are constant */
+ ir_constant *op[3];
+ foreach_list(n, &this->actual_parameters) {
+ ir_constant *constant = ((ir_rvalue *) n)->constant_expression_value();
+ if (constant == NULL)
+ return NULL;
+
+ op[num_parameters] = constant;
+
+ assert(num_parameters < 3);
+ num_parameters++;
+ }
+
+ /* Individual cases below can either:
+ * - Assign "expr" a new ir_expression to evaluate (for basic opcodes)
+ * - Fill "data" with appopriate constant data
+ * - Return an ir_constant directly.
+ */
+ void *mem_ctx = ralloc_parent(this);
+ ir_expression *expr = NULL;
+
+ ir_constant_data data;
+ memset(&data, 0, sizeof(data));
+
+ const char *callee = this->callee_name();
+ if (strcmp(callee, "abs") == 0) {
+ expr = new(mem_ctx) ir_expression(ir_unop_abs, type, op[0], NULL);
+ } else if (strcmp(callee, "all") == 0) {
+ assert(op[0]->type->is_boolean());
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ if (!op[0]->value.b[c])
+ return new(mem_ctx) ir_constant(false);
+ }
+ return new(mem_ctx) ir_constant(true);
+ } else if (strcmp(callee, "any") == 0) {
+ assert(op[0]->type->is_boolean());
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ if (op[0]->value.b[c])
+ return new(mem_ctx) ir_constant(true);
+ }
+ return new(mem_ctx) ir_constant(false);
+ } else if (strcmp(callee, "acos") == 0) {
+ assert(op[0]->type->is_float());
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.f[c] = acosf(op[0]->value.f[c]);
+ } else if (strcmp(callee, "acosh") == 0) {
+ assert(op[0]->type->is_float());
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.f[c] = acoshf(op[0]->value.f[c]);
+ } else if (strcmp(callee, "asin") == 0) {
+ assert(op[0]->type->is_float());
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.f[c] = asinf(op[0]->value.f[c]);
+ } else if (strcmp(callee, "asinh") == 0) {
+ assert(op[0]->type->is_float());
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.f[c] = asinhf(op[0]->value.f[c]);
+ } else if (strcmp(callee, "atan") == 0) {
+ assert(op[0]->type->is_float());
+ if (num_parameters == 2) {
+ assert(op[1]->type->is_float());
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.f[c] = atan2f(op[0]->value.f[c], op[1]->value.f[c]);
+ } else {
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.f[c] = atanf(op[0]->value.f[c]);
+ }
+ } else if (strcmp(callee, "atanh") == 0) {
+ assert(op[0]->type->is_float());
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.f[c] = atanhf(op[0]->value.f[c]);
+ } else if (strcmp(callee, "dFdx") == 0 || strcmp(callee, "dFdy") == 0) {
+ return ir_constant::zero(mem_ctx, this->type);
+ } else if (strcmp(callee, "ceil") == 0) {
+ expr = new(mem_ctx) ir_expression(ir_unop_ceil, type, op[0], NULL);
+ } else if (strcmp(callee, "clamp") == 0) {
+ assert(num_parameters == 3);
+ unsigned c1_inc = op[1]->type->is_scalar() ? 0 : 1;
+ unsigned c2_inc = op[2]->type->is_scalar() ? 0 : 1;
+ for (unsigned c = 0, c1 = 0, c2 = 0;
+ c < op[0]->type->components();
+ c1 += c1_inc, c2 += c2_inc, c++) {
+
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.u[c] = CLAMP(op[0]->value.u[c], op[1]->value.u[c1],
+ op[2]->value.u[c2]);
+ break;
+ case GLSL_TYPE_INT:
+ data.i[c] = CLAMP(op[0]->value.i[c], op[1]->value.i[c1],
+ op[2]->value.i[c2]);
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.f[c] = CLAMP(op[0]->value.f[c], op[1]->value.f[c1],
+ op[2]->value.f[c2]);
+ break;
+ default:
+ assert(!"Should not get here.");
+ }
+ }
+ } else if (strcmp(callee, "cos") == 0) {
+ expr = new(mem_ctx) ir_expression(ir_unop_cos, type, op[0], NULL);
+ } else if (strcmp(callee, "cosh") == 0) {
+ assert(op[0]->type->is_float());
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.f[c] = coshf(op[0]->value.f[c]);
+ } else if (strcmp(callee, "cross") == 0) {
+ assert(op[0]->type == glsl_type::vec3_type);
+ assert(op[1]->type == glsl_type::vec3_type);
+ data.f[0] = (op[0]->value.f[1] * op[1]->value.f[2] -
+ op[1]->value.f[1] * op[0]->value.f[2]);
+ data.f[1] = (op[0]->value.f[2] * op[1]->value.f[0] -
+ op[1]->value.f[2] * op[0]->value.f[0]);
+ data.f[2] = (op[0]->value.f[0] * op[1]->value.f[1] -
+ op[1]->value.f[0] * op[0]->value.f[1]);
+ } else if (strcmp(callee, "degrees") == 0) {
+ assert(op[0]->type->is_float());
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.f[c] = 180.0F / M_PI * op[0]->value.f[c];
+ } else if (strcmp(callee, "distance") == 0) {
+ assert(op[0]->type->is_float() && op[1]->type->is_float());
+ float length_squared = 0.0;
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ float t = op[0]->value.f[c] - op[1]->value.f[c];
+ length_squared += t * t;
+ }
+ return new(mem_ctx) ir_constant(sqrtf(length_squared));
+ } else if (strcmp(callee, "dot") == 0) {
+ return new(mem_ctx) ir_constant(dot(op[0], op[1]));
+ } else if (strcmp(callee, "equal") == 0) {
+ assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector());
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.b[c] = op[0]->value.u[c] == op[1]->value.u[c];
+ break;
+ case GLSL_TYPE_INT:
+ data.b[c] = op[0]->value.i[c] == op[1]->value.i[c];
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.b[c] = op[0]->value.f[c] == op[1]->value.f[c];
+ break;
+ case GLSL_TYPE_BOOL:
+ data.b[c] = op[0]->value.b[c] == op[1]->value.b[c];
+ break;
+ default:
+ assert(!"Should not get here.");
+ }
+ }
+ } else if (strcmp(callee, "exp") == 0) {
+ expr = new(mem_ctx) ir_expression(ir_unop_exp, type, op[0], NULL);
+ } else if (strcmp(callee, "exp2") == 0) {
+ expr = new(mem_ctx) ir_expression(ir_unop_exp2, type, op[0], NULL);
+ } else if (strcmp(callee, "faceforward") == 0) {
+ if (dot(op[2], op[1]) < 0)
+ return op[0];
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.f[c] = -op[0]->value.f[c];
+ } else if (strcmp(callee, "floor") == 0) {
+ expr = new(mem_ctx) ir_expression(ir_unop_floor, type, op[0], NULL);
+ } else if (strcmp(callee, "fract") == 0) {
+ expr = new(mem_ctx) ir_expression(ir_unop_fract, type, op[0], NULL);
+ } else if (strcmp(callee, "fwidth") == 0) {
+ return ir_constant::zero(mem_ctx, this->type);
+ } else if (strcmp(callee, "greaterThan") == 0) {
+ assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector());
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.b[c] = op[0]->value.u[c] > op[1]->value.u[c];
+ break;
+ case GLSL_TYPE_INT:
+ data.b[c] = op[0]->value.i[c] > op[1]->value.i[c];
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.b[c] = op[0]->value.f[c] > op[1]->value.f[c];
+ break;
+ default:
+ assert(!"Should not get here.");
+ }
+ }
+ } else if (strcmp(callee, "greaterThanEqual") == 0) {
+ assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector());
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.b[c] = op[0]->value.u[c] >= op[1]->value.u[c];
+ break;
+ case GLSL_TYPE_INT:
+ data.b[c] = op[0]->value.i[c] >= op[1]->value.i[c];
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.b[c] = op[0]->value.f[c] >= op[1]->value.f[c];
+ break;
+ default:
+ assert(!"Should not get here.");
+ }
+ }
+ } else if (strcmp(callee, "inversesqrt") == 0) {
+ expr = new(mem_ctx) ir_expression(ir_unop_rsq, type, op[0], NULL);
+ } else if (strcmp(callee, "length") == 0) {
+ return new(mem_ctx) ir_constant(sqrtf(dot(op[0], op[0])));
+ } else if (strcmp(callee, "lessThan") == 0) {
+ assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector());
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.b[c] = op[0]->value.u[c] < op[1]->value.u[c];
+ break;
+ case GLSL_TYPE_INT:
+ data.b[c] = op[0]->value.i[c] < op[1]->value.i[c];
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.b[c] = op[0]->value.f[c] < op[1]->value.f[c];
+ break;
+ default:
+ assert(!"Should not get here.");
+ }
+ }
+ } else if (strcmp(callee, "lessThanEqual") == 0) {
+ assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector());
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.b[c] = op[0]->value.u[c] <= op[1]->value.u[c];
+ break;
+ case GLSL_TYPE_INT:
+ data.b[c] = op[0]->value.i[c] <= op[1]->value.i[c];
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.b[c] = op[0]->value.f[c] <= op[1]->value.f[c];
+ break;
+ default:
+ assert(!"Should not get here.");
+ }
+ }
+ } else if (strcmp(callee, "log") == 0) {
+ expr = new(mem_ctx) ir_expression(ir_unop_log, type, op[0], NULL);
+ } else if (strcmp(callee, "log2") == 0) {
+ expr = new(mem_ctx) ir_expression(ir_unop_log2, type, op[0], NULL);
+ } else if (strcmp(callee, "matrixCompMult") == 0) {
+ assert(op[0]->type->is_float() && op[1]->type->is_float());
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.f[c] = op[0]->value.f[c] * op[1]->value.f[c];
+ } else if (strcmp(callee, "max") == 0) {
+ expr = new(mem_ctx) ir_expression(ir_binop_max, type, op[0], op[1]);
+ } else if (strcmp(callee, "min") == 0) {
+ expr = new(mem_ctx) ir_expression(ir_binop_min, type, op[0], op[1]);
+ } else if (strcmp(callee, "mix") == 0) {
+ assert(op[0]->type->is_float() && op[1]->type->is_float());
+ if (op[2]->type->is_float()) {
+ unsigned c2_inc = op[2]->type->is_scalar() ? 0 : 1;
+ unsigned components = op[0]->type->components();
+ for (unsigned c = 0, c2 = 0; c < components; c2 += c2_inc, c++) {
+ data.f[c] = op[0]->value.f[c] * (1 - op[2]->value.f[c2]) +
+ op[1]->value.f[c] * op[2]->value.f[c2];
+ }
+ } else {
+ assert(op[2]->type->is_boolean());
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.f[c] = op[op[2]->value.b[c] ? 1 : 0]->value.f[c];
+ }
+ } else if (strcmp(callee, "mod") == 0) {
+ expr = new(mem_ctx) ir_expression(ir_binop_mod, type, op[0], op[1]);
+ } else if (strcmp(callee, "normalize") == 0) {
+ assert(op[0]->type->is_float());
+ float length = sqrtf(dot(op[0], op[0]));
+
+ if (length == 0)
+ return ir_constant::zero(mem_ctx, this->type);
+
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.f[c] = op[0]->value.f[c] / length;
+ } else if (strcmp(callee, "not") == 0) {
+ expr = new(mem_ctx) ir_expression(ir_unop_logic_not, type, op[0], NULL);
+ } else if (strcmp(callee, "notEqual") == 0) {
+ assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector());
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.b[c] = op[0]->value.u[c] != op[1]->value.u[c];
+ break;
+ case GLSL_TYPE_INT:
+ data.b[c] = op[0]->value.i[c] != op[1]->value.i[c];
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.b[c] = op[0]->value.f[c] != op[1]->value.f[c];
+ break;
+ case GLSL_TYPE_BOOL:
+ data.b[c] = op[0]->value.b[c] != op[1]->value.b[c];
+ break;
+ default:
+ assert(!"Should not get here.");
+ }
+ }
+ } else if (strcmp(callee, "outerProduct") == 0) {
+ assert(op[0]->type->is_vector() && op[1]->type->is_vector());
+ const unsigned m = op[0]->type->vector_elements;
+ const unsigned n = op[1]->type->vector_elements;
+ for (unsigned j = 0; j < n; j++) {
+ for (unsigned i = 0; i < m; i++) {
+ data.f[i+m*j] = op[0]->value.f[i] * op[1]->value.f[j];
+ }
+ }
+ } else if (strcmp(callee, "pow") == 0) {
+ expr = new(mem_ctx) ir_expression(ir_binop_pow, type, op[0], op[1]);
+ } else if (strcmp(callee, "radians") == 0) {
+ assert(op[0]->type->is_float());
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.f[c] = M_PI / 180.0F * op[0]->value.f[c];
+ } else if (strcmp(callee, "reflect") == 0) {
+ assert(op[0]->type->is_float());
+ float dot_NI = dot(op[1], op[0]);
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.f[c] = op[0]->value.f[c] - 2 * dot_NI * op[1]->value.f[c];
+ } else if (strcmp(callee, "refract") == 0) {
+ const float eta = op[2]->value.f[0];
+ const float dot_NI = dot(op[1], op[0]);
+ const float k = 1.0F - eta * eta * (1.0F - dot_NI * dot_NI);
+ if (k < 0.0) {
+ return ir_constant::zero(mem_ctx, this->type);
+ } else {
+ for (unsigned c = 0; c < type->components(); c++) {
+ data.f[c] = eta * op[0]->value.f[c] - (eta * dot_NI + sqrtf(k))
+ * op[1]->value.f[c];
+ }
+ }
+ } else if (strcmp(callee, "sign") == 0) {
+ expr = new(mem_ctx) ir_expression(ir_unop_sign, type, op[0], NULL);
+ } else if (strcmp(callee, "sin") == 0) {
+ expr = new(mem_ctx) ir_expression(ir_unop_sin, type, op[0], NULL);
+ } else if (strcmp(callee, "sinh") == 0) {
+ assert(op[0]->type->is_float());
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.f[c] = sinhf(op[0]->value.f[c]);
+ } else if (strcmp(callee, "smoothstep") == 0) {
+ assert(num_parameters == 3);
+ assert(op[1]->type == op[0]->type);
+ unsigned edge_inc = op[0]->type->is_scalar() ? 0 : 1;
+ for (unsigned c = 0, e = 0; c < type->components(); e += edge_inc, c++) {
+ const float edge0 = op[0]->value.f[e];
+ const float edge1 = op[1]->value.f[e];
+ if (edge0 == edge1) {
+ data.f[c] = 0.0; /* Avoid a crash - results are undefined anyway */
+ } else {
+ const float numerator = op[2]->value.f[c] - edge0;
+ const float denominator = edge1 - edge0;
+ const float t = CLAMP(numerator/denominator, 0, 1);
+ data.f[c] = t * t * (3 - 2 * t);
+ }
+ }
+ } else if (strcmp(callee, "sqrt") == 0) {
+ expr = new(mem_ctx) ir_expression(ir_unop_sqrt, type, op[0], NULL);
+ } else if (strcmp(callee, "step") == 0) {
+ assert(op[0]->type->is_float() && op[1]->type->is_float());
+ /* op[0] (edge) may be either a scalar or a vector */
+ const unsigned c0_inc = op[0]->type->is_scalar() ? 0 : 1;
+ for (unsigned c = 0, c0 = 0; c < type->components(); c0 += c0_inc, c++)
+ data.f[c] = (op[1]->value.f[c] < op[0]->value.f[c0]) ? 0.0F : 1.0F;
+ } else if (strcmp(callee, "tan") == 0) {
+ assert(op[0]->type->is_float());
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.f[c] = tanf(op[0]->value.f[c]);
+ } else if (strcmp(callee, "tanh") == 0) {
+ assert(op[0]->type->is_float());
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.f[c] = tanhf(op[0]->value.f[c]);
+ } else if (strcmp(callee, "transpose") == 0) {
+ assert(op[0]->type->is_matrix());
+ const unsigned n = op[0]->type->vector_elements;
+ const unsigned m = op[0]->type->matrix_columns;
+ for (unsigned j = 0; j < m; j++) {
+ for (unsigned i = 0; i < n; i++) {
+ data.f[m*i+j] += op[0]->value.f[i+n*j];
+ }
+ }
+ } else {
+ /* Unsupported builtin - some are not allowed in constant expressions. */
+ return NULL;
+ }
+
+ if (expr != NULL)
+ return expr->constant_expression_value();
+
+ return new(mem_ctx) ir_constant(this->type, &data);
+}
diff --git a/mesalib/src/glsl/linker.cpp b/mesalib/src/glsl/linker.cpp
index 46cd1950c..a9a2a5e20 100644
--- a/mesalib/src/glsl/linker.cpp
+++ b/mesalib/src/glsl/linker.cpp
@@ -1,1713 +1,1713 @@
-/*
- * Copyright © 2010 Intel Corporation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice (including the next
- * paragraph) shall be included in all copies or substantial portions of the
- * Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
-/**
- * \file linker.cpp
- * GLSL linker implementation
- *
- * Given a set of shaders that are to be linked to generate a final program,
- * there are three distinct stages.
- *
- * In the first stage shaders are partitioned into groups based on the shader
- * type. All shaders of a particular type (e.g., vertex shaders) are linked
- * together.
- *
- * - Undefined references in each shader are resolve to definitions in
- * another shader.
- * - Types and qualifiers of uniforms, outputs, and global variables defined
- * in multiple shaders with the same name are verified to be the same.
- * - Initializers for uniforms and global variables defined
- * in multiple shaders with the same name are verified to be the same.
- *
- * The result, in the terminology of the GLSL spec, is a set of shader
- * executables for each processing unit.
- *
- * After the first stage is complete, a series of semantic checks are performed
- * on each of the shader executables.
- *
- * - Each shader executable must define a \c main function.
- * - Each vertex shader executable must write to \c gl_Position.
- * - Each fragment shader executable must write to either \c gl_FragData or
- * \c gl_FragColor.
- *
- * In the final stage individual shader executables are linked to create a
- * complete exectuable.
- *
- * - Types of uniforms defined in multiple shader stages with the same name
- * are verified to be the same.
- * - Initializers for uniforms defined in multiple shader stages with the
- * same name are verified to be the same.
- * - Types and qualifiers of outputs defined in one stage are verified to
- * be the same as the types and qualifiers of inputs defined with the same
- * name in a later stage.
- *
- * \author Ian Romanick <ian.d.romanick@intel.com>
- */
-#include <cstdlib>
-#include <cstdio>
-#include <cstdarg>
-#include <climits>
-
-#include "main/core.h"
-#include "glsl_symbol_table.h"
-#include "ir.h"
-#include "program.h"
-#include "program/hash_table.h"
-#include "linker.h"
-#include "ir_optimization.h"
-
-extern "C" {
-#include "main/shaderobj.h"
-}
-
-/**
- * Visitor that determines whether or not a variable is ever written.
- */
-class find_assignment_visitor : public ir_hierarchical_visitor {
-public:
- find_assignment_visitor(const char *name)
- : name(name), found(false)
- {
- /* empty */
- }
-
- virtual ir_visitor_status visit_enter(ir_assignment *ir)
- {
- ir_variable *const var = ir->lhs->variable_referenced();
-
- if (strcmp(name, var->name) == 0) {
- found = true;
- return visit_stop;
- }
-
- 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;
- }
-
-private:
- const char *name; /**< Find writes to a variable with this name. */
- bool found; /**< Was a write to the variable 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, ...)
-{
- va_list ap;
-
- ralloc_strcat(&prog->InfoLog, "error: ");
- va_start(ap, fmt);
- ralloc_vasprintf_append(&prog->InfoLog, fmt, ap);
- va_end(ap);
-}
-
-
-void
-invalidate_variable_locations(gl_shader *sh, enum ir_variable_mode mode,
- int generic_base)
-{
- foreach_list(node, sh->ir) {
- ir_variable *const var = ((ir_instruction *) node)->as_variable();
-
- if ((var == NULL) || (var->mode != (unsigned) mode))
- continue;
-
- /* Only assign locations for generic attributes / varyings / etc.
- */
- if ((var->location >= generic_base) && !var->explicit_location)
- var->location = -1;
- }
-}
-
-
-/**
- * Determine the number of attribute slots required for a particular type
- *
- * This code is here because it implements the language rules of a specific
- * GLSL version. Since it's a property of the language and not a property of
- * types in general, it doesn't really belong in glsl_type.
- */
-unsigned
-count_attribute_slots(const glsl_type *t)
-{
- /* From page 31 (page 37 of the PDF) of the GLSL 1.50 spec:
- *
- * "A scalar input counts the same amount against this limit as a vec4,
- * so applications may want to consider packing groups of four
- * unrelated float inputs together into a vector to better utilize the
- * capabilities of the underlying hardware. A matrix input will use up
- * multiple locations. The number of locations used will equal the
- * number of columns in the matrix."
- *
- * The spec does not explicitly say how arrays are counted. However, it
- * should be safe to assume the total number of slots consumed by an array
- * is the number of entries in the array multiplied by the number of slots
- * consumed by a single element of the array.
- */
-
- if (t->is_array())
- return t->array_size() * count_attribute_slots(t->element_type());
-
- if (t->is_matrix())
- return t->matrix_columns;
-
- return 1;
-}
-
-
-/**
- * Verify that a vertex shader executable meets all semantic requirements
- *
- * \param shader Vertex shader executable to be verified
- */
-bool
-validate_vertex_shader_executable(struct gl_shader_program *prog,
- struct gl_shader *shader)
-{
- if (shader == NULL)
- return true;
-
- find_assignment_visitor find("gl_Position");
- find.run(shader->ir);
- if (!find.variable_found()) {
- linker_error_printf(prog,
- "vertex shader does not write to `gl_Position'\n");
- return false;
- }
-
- return true;
-}
-
-
-/**
- * Verify that a fragment shader executable meets all semantic requirements
- *
- * \param shader Fragment shader executable to be verified
- */
-bool
-validate_fragment_shader_executable(struct gl_shader_program *prog,
- struct gl_shader *shader)
-{
- if (shader == NULL)
- return true;
-
- find_assignment_visitor frag_color("gl_FragColor");
- find_assignment_visitor frag_data("gl_FragData");
-
- frag_color.run(shader->ir);
- frag_data.run(shader->ir);
-
- if (frag_color.variable_found() && frag_data.variable_found()) {
- linker_error_printf(prog, "fragment shader writes to both "
- "`gl_FragColor' and `gl_FragData'\n");
- return false;
- }
-
- return true;
-}
-
-
-/**
- * Generate a string describing the mode of a variable
- */
-static const char *
-mode_string(const ir_variable *var)
-{
- switch (var->mode) {
- case ir_var_auto:
- return (var->read_only) ? "global constant" : "global variable";
-
- case ir_var_uniform: return "uniform";
- case ir_var_in: return "shader input";
- case ir_var_out: return "shader output";
- case ir_var_inout: return "shader inout";
-
- case ir_var_const_in:
- case ir_var_temporary:
- default:
- assert(!"Should not get here.");
- return "invalid variable";
- }
-}
-
-
-/**
- * Perform validation of global variables used across multiple shaders
- */
-bool
-cross_validate_globals(struct gl_shader_program *prog,
- struct gl_shader **shader_list,
- unsigned num_shaders,
- bool uniforms_only)
-{
- /* Examine all of the uniforms in all of the shaders and cross validate
- * them.
- */
- glsl_symbol_table variables;
- for (unsigned i = 0; i < num_shaders; i++) {
- if (shader_list[i] == NULL)
- continue;
-
- foreach_list(node, shader_list[i]->ir) {
- ir_variable *const var = ((ir_instruction *) node)->as_variable();
-
- if (var == NULL)
- continue;
-
- 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) {
- /* 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 (var->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;
- }
-
- /* Validate layout qualifiers for gl_FragDepth.
- *
- * From the AMD_conservative_depth spec:
- * "If gl_FragDepth is redeclared in any fragment shader in
- * a program, it must be redeclared in all fragment shaders in that
- * program that have static assignments to gl_FragDepth. All
- * redeclarations of gl_FragDepth in all fragment shaders in
- * a single program must have the same set of qualifiers."
- */
- if (strcmp(var->name, "gl_FragDepth") == 0) {
- bool layout_declared = var->depth_layout != ir_depth_layout_none;
- bool layout_differs = var->depth_layout != existing->depth_layout;
- if (layout_declared && layout_differs) {
- linker_error_printf(prog,
- "All redeclarations of gl_FragDepth in all fragment shaders "
- "in a single program must have the same set of qualifiers.");
- }
- if (var->used && layout_differs) {
- linker_error_printf(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");
- }
- }
-
- /* FINISHME: Handle non-constant initializers.
- */
- if (var->constant_value != NULL) {
- if (existing->constant_value != NULL) {
- if (!var->constant_value->has_value(existing->constant_value)) {
- linker_error_printf(prog, "initializers for %s "
- "`%s' have differing values\n",
- mode_string(var), var->name);
- return false;
- }
- } else
- /* If the first-seen instance of a particular uniform did not
- * 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 =
- var->constant_value->clone(ralloc_parent(existing), NULL);
- }
-
- if (existing->invariant != var->invariant) {
- linker_error_printf(prog, "declarations for %s `%s' have "
- "mismatching invariant qualifiers\n",
- mode_string(var), var->name);
- return false;
- }
- if (existing->centroid != var->centroid) {
- linker_error_printf(prog, "declarations for %s `%s' have "
- "mismatching centroid qualifiers\n",
- mode_string(var), var->name);
- return false;
- }
- } else
- variables.add_variable(var);
- }
- }
-
- return true;
-}
-
-
-/**
- * Perform validation of uniforms used across multiple shader stages
- */
-bool
-cross_validate_uniforms(struct gl_shader_program *prog)
-{
- return cross_validate_globals(prog, prog->_LinkedShaders,
- MESA_SHADER_TYPES, true);
-}
-
-
-/**
- * Validate that outputs from one stage match inputs of another
- */
-bool
-cross_validate_outputs_to_inputs(struct gl_shader_program *prog,
- gl_shader *producer, gl_shader *consumer)
-{
- glsl_symbol_table parameters;
- /* FINISHME: Figure these out dynamically. */
- const char *const producer_stage = "vertex";
- const char *const consumer_stage = "fragment";
-
- /* Find all shader outputs in the "producer" stage.
- */
- foreach_list(node, producer->ir) {
- ir_variable *const var = ((ir_instruction *) node)->as_variable();
-
- /* FINISHME: For geometry shaders, this should also look for inout
- * FINISHME: variables.
- */
- if ((var == NULL) || (var->mode != ir_var_out))
- continue;
-
- parameters.add_variable(var);
- }
-
-
- /* Find all shader inputs in the "consumer" stage. Any variables that have
- * matching outputs already in the symbol table must have the same type and
- * qualifiers.
- */
- foreach_list(node, consumer->ir) {
- ir_variable *const input = ((ir_instruction *) node)->as_variable();
-
- /* FINISHME: For geometry shaders, this should also look for inout
- * FINISHME: variables.
- */
- if ((input == NULL) || (input->mode != ir_var_in))
- continue;
-
- ir_variable *const output = parameters.get_variable(input->name);
- if (output != NULL) {
- /* Check that the types match between stages.
- */
- if (input->type != output->type) {
- /* There is a bit of a special case for gl_TexCoord. This
- * built-in is unsized by default. Appliations that variable
- * access it must redeclare it with a size. There is some
- * language in the GLSL spec that implies the fragment shader
- * and vertex shader do not have to agree on this size. Other
- * driver behave this way, and one or two applications seem to
- * rely on it.
- *
- * Neither declaration needs to be modified here because the array
- * sizes are fixed later when update_array_sizes is called.
- *
- * From page 48 (page 54 of the PDF) of the GLSL 1.10 spec:
- *
- * "Unlike user-defined varying variables, the built-in
- * varying variables don't have a strict one-to-one
- * correspondence between the vertex language and the
- * fragment language."
- */
- if (!output->type->is_array()
- || (strncmp("gl_", output->name, 3) != 0)) {
- linker_error_printf(prog,
- "%s shader output `%s' declared as "
- "type `%s', but %s shader input declared "
- "as type `%s'\n",
- producer_stage, output->name,
- output->type->name,
- consumer_stage, input->type->name);
- return false;
- }
- }
-
- /* Check that all of the qualifiers match between stages.
- */
- if (input->centroid != output->centroid) {
- linker_error_printf(prog,
- "%s shader output `%s' %s centroid qualifier, "
- "but %s shader input %s centroid qualifier\n",
- producer_stage,
- output->name,
- (output->centroid) ? "has" : "lacks",
- consumer_stage,
- (input->centroid) ? "has" : "lacks");
- return false;
- }
-
- if (input->invariant != output->invariant) {
- linker_error_printf(prog,
- "%s shader output `%s' %s invariant qualifier, "
- "but %s shader input %s invariant qualifier\n",
- producer_stage,
- output->name,
- (output->invariant) ? "has" : "lacks",
- consumer_stage,
- (input->invariant) ? "has" : "lacks");
- return false;
- }
-
- if (input->interpolation != output->interpolation) {
- linker_error_printf(prog,
- "%s shader output `%s' specifies %s "
- "interpolation qualifier, "
- "but %s shader input specifies %s "
- "interpolation qualifier\n",
- producer_stage,
- output->name,
- output->interpolation_string(),
- consumer_stage,
- input->interpolation_string());
- return false;
- }
- }
- }
-
- return true;
-}
-
-
-/**
- * Populates a shaders symbol table with all global declarations
- */
-static void
-populate_symbol_table(gl_shader *sh)
-{
- sh->symbols = new(sh) glsl_symbol_table;
-
- foreach_list(node, sh->ir) {
- ir_instruction *const inst = (ir_instruction *) node;
- ir_variable *var;
- ir_function *func;
-
- if ((func = inst->as_function()) != NULL) {
- sh->symbols->add_function(func);
- } else if ((var = inst->as_variable()) != NULL) {
- sh->symbols->add_variable(var);
- }
- }
-}
-
-
-/**
- * Remap variables referenced in an instruction tree
- *
- * This is used when instruction trees are cloned from one shader and placed in
- * another. These trees will contain references to \c ir_variable nodes that
- * do not exist in the target shader. This function finds these \c ir_variable
- * references and replaces the references with matching variables in the target
- * shader.
- *
- * If there is no matching variable in the target shader, a clone of the
- * \c ir_variable is made and added to the target shader. The new variable is
- * added to \b both the instruction stream and the symbol table.
- *
- * \param inst IR tree that is to be processed.
- * \param symbols Symbol table containing global scope symbols in the
- * linked shader.
- * \param instructions Instruction stream where new variable declarations
- * should be added.
- */
-void
-remap_variables(ir_instruction *inst, struct gl_shader *target,
- hash_table *temps)
-{
- class remap_visitor : public ir_hierarchical_visitor {
- public:
- remap_visitor(struct gl_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->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(this->target, NULL);
-
- this->symbols->add_variable(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(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 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.
- *
- * \param instructions Source instruction stream
- * \param last Instruction after which new instructions should be
- * inserted in the target instruction stream
- * \param make_copies Flag selecting whether instructions in \c instructions
- * should be copied (via \c ir_instruction::clone) into the
- * target list or moved.
- *
- * \return
- * The new "last" instruction in the target instruction stream. This pointer
- * is suitable for use as the \c last parameter of a later call to this
- * function.
- */
-exec_node *
-move_non_declarations(exec_list *instructions, exec_node *last,
- bool make_copies, gl_shader *target)
-{
- 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_function())
- continue;
-
- ir_variable *var = inst->as_variable();
- if ((var != NULL) && (var->mode != ir_var_temporary))
- continue;
-
- assert(inst->as_assignment()
- || ((var != NULL) && (var->mode == ir_var_temporary)));
-
- if (make_copies) {
- inst = inst->clone(target, NULL);
-
- if (var != NULL)
- hash_table_insert(temps, inst, var);
- else
- remap_variables(inst, target, temps);
- } else {
- inst->remove();
- }
-
- last->insert_after(inst);
- last = inst;
- }
-
- if (make_copies)
- hash_table_dtor(temps);
-
- return last;
-}
-
-/**
- * Get the function signature for main from a shader
- */
-static ir_function_signature *
-get_main_function_signature(gl_shader *sh)
-{
- ir_function *const f = sh->symbols->get_function("main");
- if (f != NULL) {
- exec_list void_parameters;
-
- /* Look for the 'void main()' signature and ensure that it's defined.
- * This keeps the linker from accidentally pick a shader that just
- * contains a prototype for main.
- *
- * We don't have to check for multiple definitions of main (in multiple
- * shaders) because that would have already been caught above.
- */
- ir_function_signature *sig = f->matching_signature(&void_parameters);
- if ((sig != NULL) && sig->is_defined) {
- return sig;
- }
- }
-
- return NULL;
-}
-
-
-/**
- * Combine a group of shaders for a single stage to generate a linked shader
- *
- * \note
- * If this function is supplied a single shader, it is cloned, and the new
- * shader is returned.
- */
-static struct gl_shader *
-link_intrastage_shaders(void *mem_ctx,
- struct gl_context *ctx,
- struct gl_shader_program *prog,
- struct gl_shader **shader_list,
- unsigned num_shaders)
-{
- /* Check that global variables defined in multiple shaders are consistent.
- */
- if (!cross_validate_globals(prog, shader_list, num_shaders, false))
- return NULL;
-
- /* Check that there is only a single definition of each function signature
- * across all shaders.
- */
- for (unsigned i = 0; i < (num_shaders - 1); i++) {
- foreach_list(node, shader_list[i]->ir) {
- ir_function *const f = ((ir_instruction *) 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 the other shader has no function (and therefore no function
- * signatures) with the same name, skip to the next shader.
- */
- if (other == NULL)
- continue;
-
- foreach_iter (exec_list_iterator, iter, *f) {
- ir_function_signature *sig =
- (ir_function_signature *) iter.get();
-
- 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_builtin) {
- linker_error_printf(prog,
- "function `%s' is multiply defined",
- f->name);
- return NULL;
- }
- }
- }
- }
- }
-
- /* Find the shader that defines main, and make a clone of it.
- *
- * Starting with the clone, search for undefined references. If one is
- * found, find the shader that defines it. Clone the reference and add
- * it to the shader. Repeat until there are no undefined references or
- * until a reference cannot be resolved.
- */
- gl_shader *main = NULL;
- for (unsigned i = 0; i < num_shaders; i++) {
- if (get_main_function_signature(shader_list[i]) != NULL) {
- main = shader_list[i];
- break;
- }
- }
-
- if (main == NULL) {
- linker_error_printf(prog, "%s shader lacks `main'\n",
- (shader_list[0]->Type == GL_VERTEX_SHADER)
- ? "vertex" : "fragment");
- return NULL;
- }
-
- gl_shader *linked = ctx->Driver.NewShader(NULL, 0, main->Type);
- linked->ir = new(linked) exec_list;
- clone_ir_list(mem_ctx, linked->ir, main->ir);
-
- populate_symbol_table(linked);
-
- /* The a pointer to the main function in the final linked shader (i.e., the
- * copy of the original shader that contained the main function).
- */
- ir_function_signature *const main_sig = get_main_function_signature(linked);
-
- /* Move any instructions other than variable declarations or function
- * declarations into main.
- */
- 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);
- }
-
- /* 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);
-
- if (!link_function_calls(prog, linked, linking_shaders,
- num_linking_shaders)) {
- ctx->Driver.DeleteShader(ctx, linked);
- linked = NULL;
- }
-
- free(linking_shaders);
-
- /* Make a pass over all variable declarations to ensure that arrays with
- * unspecified sizes have a size specified. The size is inferred from 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);
-
- assert(type != NULL);
- var->type = type;
- }
-
- return visit_continue;
- }
- } v;
-
- v.run(linked->ir);
- }
-
- return linked;
-}
-
-
-struct uniform_node {
- exec_node link;
- struct gl_uniform *u;
- 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 < MESA_SHADER_TYPES; i++) {
- if (prog->_LinkedShaders[i] == NULL)
- continue;
-
- 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 < MESA_SHADER_TYPES; j++) {
- if (prog->_LinkedShaders[j] == NULL)
- continue;
-
- 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 = ralloc_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 = ralloc_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)
-{
- /* */
- exec_list uniforms;
- unsigned total_uniforms = 0;
- hash_table *ht = hash_table_ctor(32, hash_table_string_hash,
- hash_table_string_compare);
- void *mem_ctx = ralloc_context(NULL);
-
- for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) {
- if (prog->_LinkedShaders[i] == NULL)
- continue;
-
- unsigned next_position = 0;
-
- foreach_list(node, prog->_LinkedShaders[i]->ir) {
- ir_variable *const var = ((ir_instruction *) node)->as_variable();
-
- if ((var == NULL) || (var->mode != ir_var_uniform))
- continue;
-
- 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;
- }
-
- var->location = next_position;
- add_uniform(mem_ctx, &uniforms, ht, var->name, var->type,
- prog->_LinkedShaders[i]->Type,
- &next_position, &total_uniforms);
- }
- }
-
- ralloc_free(mem_ctx);
-
- gl_uniform_list *ul = (gl_uniform_list *)
- calloc(1, sizeof(gl_uniform_list));
-
- ul->Size = total_uniforms;
- ul->NumUniforms = total_uniforms;
- ul->Uniforms = (gl_uniform *) calloc(total_uniforms, sizeof(gl_uniform));
-
- unsigned idx = 0;
- uniform_node *next;
- for (uniform_node *node = (uniform_node *) uniforms.head
- ; node->link.next != NULL
- ; node = next) {
- next = (uniform_node *) node->link.next;
-
- node->link.remove();
- memcpy(&ul->Uniforms[idx], node->u, sizeof(gl_uniform));
- idx++;
-
- free(node->u);
- free(node);
- }
-
- hash_table_dtor(ht);
-
- prog->Uniforms = ul;
-}
-
-
-/**
- * Find a contiguous set of available bits in a bitmask
- *
- * \param used_mask Bits representing used (1) and unused (0) locations
- * \param needed_count Number of contiguous bits needed.
- *
- * \return
- * Base location of the available bits on success or -1 on failure.
- */
-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;
-
- /* The comparison to 32 is redundant, but without it GCC emits "warning:
- * cannot optimize possibly infinite loops" for the loop below.
- */
- 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;
-}
-
-
-bool
-assign_attribute_locations(gl_shader_program *prog, unsigned max_attribute_index)
-{
- /* Mark invalid attribute locations as being used.
- */
- unsigned used_locations = (max_attribute_index >= 32)
- ? ~0 : ~((1 << max_attribute_index) - 1);
-
- gl_shader *const sh = prog->_LinkedShaders[0];
- assert(sh->Type == GL_VERTEX_SHADER);
-
- /* Operate in a total of four passes.
- *
- * 1. Invalidate the location assignments for all vertex shader inputs.
- *
- * 2. Assign locations for inputs that have user-defined (via
- * glBindVertexAttribLocation) locatoins.
- *
- * 3. Sort the attributes without assigned locations by number of slots
- * required in decreasing order. Fragmentation caused by attribute
- * locations assigned by the application may prevent large attributes
- * from having enough contiguous space.
- *
- * 4. Assign locations to any inputs without assigned locations.
- */
-
- invalidate_variable_locations(sh, ir_var_in, VERT_ATTRIB_GENERIC0);
-
- if (prog->Attributes != NULL) {
- for (unsigned i = 0; i < prog->Attributes->NumParameters; i++) {
- ir_variable *const var =
- sh->symbols->get_variable(prog->Attributes->Parameters[i].Name);
-
- /* Note: attributes that occupy multiple slots, such as arrays or
- * matrices, may appear in the attrib array multiple times.
- */
- if ((var == NULL) || (var->location != -1))
- continue;
-
- /* From page 61 of the OpenGL 4.0 spec:
- *
- * "LinkProgram will fail if the attribute bindings assigned by
- * BindAttribLocation do not leave not enough space to assign a
- * location for an active matrix attribute or an active attribute
- * array, both of which require multiple contiguous generic
- * attributes."
- *
- * Previous versions of the spec contain similar language but omit the
- * bit about attribute arrays.
- *
- * Page 61 of the OpenGL 4.0 spec also says:
- *
- * "It is possible for an application to bind more than one
- * attribute name to the same location. This is referred to as
- * aliasing. This will only work if only one of the aliased
- * attributes is active in the executable program, or if no path
- * through the shader consumes more than one attribute of a set
- * of attributes aliased to the same location. A link error can
- * occur if the linker determines that every path through the
- * shader consumes multiple aliased attributes, but
- * implementations are not required to generate an error in this
- * case."
- *
- * These two paragraphs are either somewhat contradictory, or I don't
- * fully understand one or both of them.
- */
- /* FINISHME: The code as currently written does not support attribute
- * FINISHME: location aliasing (see comment above).
- */
- const int attr = prog->Attributes->Parameters[i].StateIndexes[0];
- const unsigned slots = count_attribute_slots(var->type);
-
- /* Mask representing the contiguous slots that will be used by this
- * attribute.
- */
- const unsigned use_mask = (1 << slots) - 1;
-
- /* Generate a link error if the set of bits requested for this
- * attribute overlaps any previously allocated bits.
- */
- if ((~(use_mask << attr) & used_locations) != used_locations) {
- linker_error_printf(prog,
- "insufficient contiguous attribute locations "
- "available for vertex shader input `%s'",
- var->name);
- return false;
- }
-
- var->location = VERT_ATTRIB_GENERIC0 + attr;
- used_locations |= (use_mask << attr);
- }
- }
-
- /* Temporary storage for the set of attributes that need locations assigned.
- */
- struct temp_attr {
- unsigned slots;
- ir_variable *var;
-
- /* Used below in the call to qsort. */
- 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;
-
- /* Reversed because we want a descending order sort below. */
- return r->slots - l->slots;
- }
- } to_assign[16];
-
- unsigned num_attr = 0;
-
- foreach_list(node, sh->ir) {
- ir_variable *const var = ((ir_instruction *) node)->as_variable();
-
- 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)
- continue;
-
- to_assign[num_attr].slots = count_attribute_slots(var->type);
- to_assign[num_attr].var = var;
- num_attr++;
- }
-
- /* If all of the attributes were assigned locations by the application (or
- * are built-in attributes with fixed locations), return early. This should
- * be the common case.
- */
- if (num_attr == 0)
- return true;
-
- qsort(to_assign, num_attr, sizeof(to_assign[0]), temp_attr::compare);
-
- /* VERT_ATTRIB_GENERIC0 is a psdueo-alias for VERT_ATTRIB_POS. It can only
- * be explicitly assigned by via glBindAttribLocation. Mark it as reserved
- * to prevent it from being automatically allocated below.
- */
- 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
- * attribute.
- */
- const unsigned use_mask = (1 << to_assign[i].slots) - 1;
-
- int location = find_available_slots(used_locations, to_assign[i].slots);
-
- if (location < 0) {
- linker_error_printf(prog,
- "insufficient contiguous attribute locations "
- "available for vertex shader input `%s'",
- to_assign[i].var->name);
- return false;
- }
-
- to_assign[i].var->location = VERT_ATTRIB_GENERIC0 + location;
- used_locations |= (use_mask << location);
- }
-
- return true;
-}
-
-
-/**
- * Demote shader inputs and outputs that are not used in other stages
- */
-void
-demote_shader_inputs_and_outputs(gl_shader *sh, enum ir_variable_mode mode)
-{
- foreach_list(node, sh->ir) {
- ir_variable *const var = ((ir_instruction *) node)->as_variable();
-
- if ((var == NULL) || (var->mode != int(mode)))
- continue;
-
- /* A shader 'in' or 'out' variable is only really an input or output if
- * its value is used by other shader stages. This will cause the variable
- * to have a location assigned.
- */
- 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;
- unsigned input_index = FRAG_ATTRIB_VAR0;
-
- /* Operate in a total of three passes.
- *
- * 1. Assign locations for any matching inputs and outputs.
- *
- * 2. Mark output variables in the producer that do not have locations as
- * not being outputs. This lets the optimizer eliminate them.
- *
- * 3. Mark input variables in the consumer that do not have locations as
- * not being inputs. This lets the optimizer eliminate them.
- */
-
- invalidate_variable_locations(producer, ir_var_out, VERT_RESULT_VAR0);
- invalidate_variable_locations(consumer, ir_var_in, FRAG_ATTRIB_VAR0);
-
- foreach_list(node, producer->ir) {
- ir_variable *const output_var = ((ir_instruction *) node)->as_variable();
-
- if ((output_var == NULL) || (output_var->mode != ir_var_out)
- || (output_var->location != -1))
- continue;
-
- ir_variable *const input_var =
- consumer->symbols->get_variable(output_var->name);
-
- if ((input_var == NULL) || (input_var->mode != ir_var_in))
- continue;
-
- assert(input_var->location == -1);
-
- output_var->location = output_index;
- input_var->location = input_index;
-
- /* FINISHME: Support for "varying" records in GLSL 1.50. */
- assert(!output_var->type->is_record());
-
- if (output_var->type->is_array()) {
- const unsigned slots = output_var->type->length
- * output_var->type->fields.array->matrix_columns;
-
- output_index += slots;
- input_index += slots;
- } else {
- const unsigned slots = output_var->type->matrix_columns;
-
- output_index += slots;
- input_index += slots;
- }
- }
-
- foreach_list(node, consumer->ir) {
- ir_variable *const var = ((ir_instruction *) node)->as_variable();
-
- if ((var == NULL) || (var->mode != ir_var_in))
- continue;
-
- 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_context *ctx, struct gl_shader_program *prog)
-{
- void *mem_ctx = ralloc_context(NULL); // temporary linker context
-
- prog->LinkStatus = false;
- prog->Validated = false;
- prog->_Used = false;
-
- if (prog->InfoLog != NULL)
- ralloc_free(prog->InfoLog);
-
- prog->InfoLog = ralloc_strdup(NULL, "");
-
- /* Separate the shaders into groups based on their type.
- */
- struct gl_shader **vert_shader_list;
- unsigned num_vert_shaders = 0;
- struct gl_shader **frag_shader_list;
- unsigned num_frag_shaders = 0;
-
- vert_shader_list = (struct gl_shader **)
- 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];
- num_vert_shaders++;
- break;
- case GL_FRAGMENT_SHADER:
- frag_shader_list[num_frag_shaders] = prog->Shaders[i];
- num_frag_shaders++;
- break;
- case GL_GEOMETRY_SHADER:
- /* FINISHME: Support geometry shaders. */
- assert(prog->Shaders[i]->Type != GL_GEOMETRY_SHADER);
- break;
- }
- }
-
- /* 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 < MESA_SHADER_TYPES; i++) {
- if (prog->_LinkedShaders[i] != NULL)
- ctx->Driver.DeleteShader(ctx, prog->_LinkedShaders[i]);
-
- prog->_LinkedShaders[i] = NULL;
- }
-
- /* Link all shaders for a particular stage and validate the result.
- */
- if (num_vert_shaders > 0) {
- gl_shader *const sh =
- link_intrastage_shaders(mem_ctx, ctx, prog, vert_shader_list,
- num_vert_shaders);
-
- if (sh == NULL)
- goto done;
-
- if (!validate_vertex_shader_executable(prog, sh))
- goto done;
-
- _mesa_reference_shader(ctx, &prog->_LinkedShaders[MESA_SHADER_VERTEX],
- sh);
- }
-
- if (num_frag_shaders > 0) {
- gl_shader *const sh =
- link_intrastage_shaders(mem_ctx, ctx, prog, frag_shader_list,
- num_frag_shaders);
-
- if (sh == NULL)
- goto done;
-
- if (!validate_fragment_shader_executable(prog, sh))
- goto done;
-
- _mesa_reference_shader(ctx, &prog->_LinkedShaders[MESA_SHADER_FRAGMENT],
- sh);
- }
-
- /* Here begins the inter-stage linking phase. Some initial validation is
- * performed, then locations are assigned for uniforms, attributes, and
- * varyings.
- */
- if (cross_validate_uniforms(prog)) {
- unsigned prev;
-
- for (prev = 0; prev < MESA_SHADER_TYPES; prev++) {
- if (prog->_LinkedShaders[prev] != NULL)
- break;
- }
-
- /* Validate the inputs of each stage with the output of the preceeding
- * stage.
- */
- for (unsigned i = prev + 1; i < MESA_SHADER_TYPES; i++) {
- if (prog->_LinkedShaders[i] == NULL)
- continue;
-
- if (!cross_validate_outputs_to_inputs(prog,
- prog->_LinkedShaders[prev],
- prog->_LinkedShaders[i]))
- goto done;
-
- prev = i;
- }
-
- prog->LinkStatus = true;
- }
-
- /* 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 < MESA_SHADER_TYPES; i++) {
- if (prog->_LinkedShaders[i] == NULL)
- continue;
-
- while (do_common_optimization(prog->_LinkedShaders[i]->ir, true, 32))
- ;
- }
-
- update_array_sizes(prog);
-
- assign_uniform_locations(prog);
-
- if (prog->_LinkedShaders[MESA_SHADER_VERTEX] != NULL) {
- /* 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)) {
- prog->LinkStatus = false;
- goto done;
- }
- }
-
- unsigned prev;
- for (prev = 0; prev < MESA_SHADER_TYPES; prev++) {
- if (prog->_LinkedShaders[prev] != NULL)
- break;
- }
-
- for (unsigned i = prev + 1; i < MESA_SHADER_TYPES; i++) {
- if (prog->_LinkedShaders[i] == NULL)
- continue;
-
- assign_varying_locations(prog,
- prog->_LinkedShaders[prev],
- prog->_LinkedShaders[i]);
- prev = i;
- }
-
- if (prog->_LinkedShaders[MESA_SHADER_VERTEX] != NULL) {
- demote_shader_inputs_and_outputs(prog->_LinkedShaders[MESA_SHADER_VERTEX],
- ir_var_out);
- }
-
- if (prog->_LinkedShaders[MESA_SHADER_GEOMETRY] != NULL) {
- gl_shader *const sh = prog->_LinkedShaders[MESA_SHADER_GEOMETRY];
-
- demote_shader_inputs_and_outputs(sh, ir_var_in);
- demote_shader_inputs_and_outputs(sh, ir_var_inout);
- demote_shader_inputs_and_outputs(sh, ir_var_out);
- }
-
- if (prog->_LinkedShaders[MESA_SHADER_FRAGMENT] != NULL) {
- gl_shader *const sh = prog->_LinkedShaders[MESA_SHADER_FRAGMENT];
-
- demote_shader_inputs_and_outputs(sh, ir_var_in);
- }
-
- /* OpenGL ES requires that a vertex shader and a fragment shader both be
- * present in a linked program. By checking for use of shading language
- * version 1.00, we also catch the GL_ARB_ES2_compatibility case.
- */
- if (ctx->API == API_OPENGLES2 || prog->Version == 100) {
- if (prog->_LinkedShaders[MESA_SHADER_VERTEX] == NULL) {
- linker_error_printf(prog, "program lacks a vertex shader\n");
- prog->LinkStatus = false;
- } else if (prog->_LinkedShaders[MESA_SHADER_FRAGMENT] == NULL) {
- linker_error_printf(prog, "program lacks a fragment shader\n");
- prog->LinkStatus = false;
- }
- }
-
- /* FINISHME: Assign fragment shader output locations. */
-
-done:
- free(vert_shader_list);
-
- for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) {
- if (prog->_LinkedShaders[i] == NULL)
- continue;
-
- /* Retain any live IR, but trash the rest. */
- reparent_ir(prog->_LinkedShaders[i]->ir, prog->_LinkedShaders[i]->ir);
- }
-
- ralloc_free(mem_ctx);
-}
+/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * \file linker.cpp
+ * GLSL linker implementation
+ *
+ * Given a set of shaders that are to be linked to generate a final program,
+ * there are three distinct stages.
+ *
+ * In the first stage shaders are partitioned into groups based on the shader
+ * type. All shaders of a particular type (e.g., vertex shaders) are linked
+ * together.
+ *
+ * - Undefined references in each shader are resolve to definitions in
+ * another shader.
+ * - Types and qualifiers of uniforms, outputs, and global variables defined
+ * in multiple shaders with the same name are verified to be the same.
+ * - Initializers for uniforms and global variables defined
+ * in multiple shaders with the same name are verified to be the same.
+ *
+ * The result, in the terminology of the GLSL spec, is a set of shader
+ * executables for each processing unit.
+ *
+ * After the first stage is complete, a series of semantic checks are performed
+ * on each of the shader executables.
+ *
+ * - Each shader executable must define a \c main function.
+ * - Each vertex shader executable must write to \c gl_Position.
+ * - Each fragment shader executable must write to either \c gl_FragData or
+ * \c gl_FragColor.
+ *
+ * In the final stage individual shader executables are linked to create a
+ * complete exectuable.
+ *
+ * - Types of uniforms defined in multiple shader stages with the same name
+ * are verified to be the same.
+ * - Initializers for uniforms defined in multiple shader stages with the
+ * same name are verified to be the same.
+ * - Types and qualifiers of outputs defined in one stage are verified to
+ * be the same as the types and qualifiers of inputs defined with the same
+ * name in a later stage.
+ *
+ * \author Ian Romanick <ian.d.romanick@intel.com>
+ */
+#include <cstdlib>
+#include <cstdio>
+#include <cstdarg>
+#include <climits>
+
+#include "main/core.h"
+#include "glsl_symbol_table.h"
+#include "ir.h"
+#include "program.h"
+#include "program/hash_table.h"
+#include "linker.h"
+#include "ir_optimization.h"
+
+extern "C" {
+#include "main/shaderobj.h"
+}
+
+/**
+ * Visitor that determines whether or not a variable is ever written.
+ */
+class find_assignment_visitor : public ir_hierarchical_visitor {
+public:
+ find_assignment_visitor(const char *name)
+ : name(name), found(false)
+ {
+ /* empty */
+ }
+
+ virtual ir_visitor_status visit_enter(ir_assignment *ir)
+ {
+ ir_variable *const var = ir->lhs->variable_referenced();
+
+ if (strcmp(name, var->name) == 0) {
+ found = true;
+ return visit_stop;
+ }
+
+ 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;
+ }
+
+private:
+ const char *name; /**< Find writes to a variable with this name. */
+ bool found; /**< Was a write to the variable 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, ...)
+{
+ va_list ap;
+
+ ralloc_strcat(&prog->InfoLog, "error: ");
+ va_start(ap, fmt);
+ ralloc_vasprintf_append(&prog->InfoLog, fmt, ap);
+ va_end(ap);
+}
+
+
+void
+invalidate_variable_locations(gl_shader *sh, enum ir_variable_mode mode,
+ int generic_base)
+{
+ foreach_list(node, sh->ir) {
+ ir_variable *const var = ((ir_instruction *) node)->as_variable();
+
+ if ((var == NULL) || (var->mode != (unsigned) mode))
+ continue;
+
+ /* Only assign locations for generic attributes / varyings / etc.
+ */
+ if ((var->location >= generic_base) && !var->explicit_location)
+ var->location = -1;
+ }
+}
+
+
+/**
+ * Determine the number of attribute slots required for a particular type
+ *
+ * This code is here because it implements the language rules of a specific
+ * GLSL version. Since it's a property of the language and not a property of
+ * types in general, it doesn't really belong in glsl_type.
+ */
+unsigned
+count_attribute_slots(const glsl_type *t)
+{
+ /* From page 31 (page 37 of the PDF) of the GLSL 1.50 spec:
+ *
+ * "A scalar input counts the same amount against this limit as a vec4,
+ * so applications may want to consider packing groups of four
+ * unrelated float inputs together into a vector to better utilize the
+ * capabilities of the underlying hardware. A matrix input will use up
+ * multiple locations. The number of locations used will equal the
+ * number of columns in the matrix."
+ *
+ * The spec does not explicitly say how arrays are counted. However, it
+ * should be safe to assume the total number of slots consumed by an array
+ * is the number of entries in the array multiplied by the number of slots
+ * consumed by a single element of the array.
+ */
+
+ if (t->is_array())
+ return t->array_size() * count_attribute_slots(t->element_type());
+
+ if (t->is_matrix())
+ return t->matrix_columns;
+
+ return 1;
+}
+
+
+/**
+ * Verify that a vertex shader executable meets all semantic requirements
+ *
+ * \param shader Vertex shader executable to be verified
+ */
+bool
+validate_vertex_shader_executable(struct gl_shader_program *prog,
+ struct gl_shader *shader)
+{
+ if (shader == NULL)
+ return true;
+
+ find_assignment_visitor find("gl_Position");
+ find.run(shader->ir);
+ if (!find.variable_found()) {
+ linker_error_printf(prog,
+ "vertex shader does not write to `gl_Position'\n");
+ return false;
+ }
+
+ return true;
+}
+
+
+/**
+ * Verify that a fragment shader executable meets all semantic requirements
+ *
+ * \param shader Fragment shader executable to be verified
+ */
+bool
+validate_fragment_shader_executable(struct gl_shader_program *prog,
+ struct gl_shader *shader)
+{
+ if (shader == NULL)
+ return true;
+
+ find_assignment_visitor frag_color("gl_FragColor");
+ find_assignment_visitor frag_data("gl_FragData");
+
+ frag_color.run(shader->ir);
+ frag_data.run(shader->ir);
+
+ if (frag_color.variable_found() && frag_data.variable_found()) {
+ linker_error_printf(prog, "fragment shader writes to both "
+ "`gl_FragColor' and `gl_FragData'\n");
+ return false;
+ }
+
+ return true;
+}
+
+
+/**
+ * Generate a string describing the mode of a variable
+ */
+static const char *
+mode_string(const ir_variable *var)
+{
+ switch (var->mode) {
+ case ir_var_auto:
+ return (var->read_only) ? "global constant" : "global variable";
+
+ case ir_var_uniform: return "uniform";
+ case ir_var_in: return "shader input";
+ case ir_var_out: return "shader output";
+ case ir_var_inout: return "shader inout";
+
+ case ir_var_const_in:
+ case ir_var_temporary:
+ default:
+ assert(!"Should not get here.");
+ return "invalid variable";
+ }
+}
+
+
+/**
+ * Perform validation of global variables used across multiple shaders
+ */
+bool
+cross_validate_globals(struct gl_shader_program *prog,
+ struct gl_shader **shader_list,
+ unsigned num_shaders,
+ bool uniforms_only)
+{
+ /* Examine all of the uniforms in all of the shaders and cross validate
+ * them.
+ */
+ glsl_symbol_table variables;
+ for (unsigned i = 0; i < num_shaders; i++) {
+ if (shader_list[i] == NULL)
+ continue;
+
+ foreach_list(node, shader_list[i]->ir) {
+ ir_variable *const var = ((ir_instruction *) node)->as_variable();
+
+ if (var == NULL)
+ continue;
+
+ 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) {
+ /* 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 (var->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;
+ }
+
+ /* Validate layout qualifiers for gl_FragDepth.
+ *
+ * From the AMD_conservative_depth spec:
+ * "If gl_FragDepth is redeclared in any fragment shader in
+ * a program, it must be redeclared in all fragment shaders in that
+ * program that have static assignments to gl_FragDepth. All
+ * redeclarations of gl_FragDepth in all fragment shaders in
+ * a single program must have the same set of qualifiers."
+ */
+ if (strcmp(var->name, "gl_FragDepth") == 0) {
+ bool layout_declared = var->depth_layout != ir_depth_layout_none;
+ bool layout_differs = var->depth_layout != existing->depth_layout;
+ if (layout_declared && layout_differs) {
+ linker_error_printf(prog,
+ "All redeclarations of gl_FragDepth in all fragment shaders "
+ "in a single program must have the same set of qualifiers.");
+ }
+ if (var->used && layout_differs) {
+ linker_error_printf(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");
+ }
+ }
+
+ /* FINISHME: Handle non-constant initializers.
+ */
+ if (var->constant_value != NULL) {
+ if (existing->constant_value != NULL) {
+ if (!var->constant_value->has_value(existing->constant_value)) {
+ linker_error_printf(prog, "initializers for %s "
+ "`%s' have differing values\n",
+ mode_string(var), var->name);
+ return false;
+ }
+ } else
+ /* If the first-seen instance of a particular uniform did not
+ * 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 =
+ var->constant_value->clone(ralloc_parent(existing), NULL);
+ }
+
+ if (existing->invariant != var->invariant) {
+ linker_error_printf(prog, "declarations for %s `%s' have "
+ "mismatching invariant qualifiers\n",
+ mode_string(var), var->name);
+ return false;
+ }
+ if (existing->centroid != var->centroid) {
+ linker_error_printf(prog, "declarations for %s `%s' have "
+ "mismatching centroid qualifiers\n",
+ mode_string(var), var->name);
+ return false;
+ }
+ } else
+ variables.add_variable(var);
+ }
+ }
+
+ return true;
+}
+
+
+/**
+ * Perform validation of uniforms used across multiple shader stages
+ */
+bool
+cross_validate_uniforms(struct gl_shader_program *prog)
+{
+ return cross_validate_globals(prog, prog->_LinkedShaders,
+ MESA_SHADER_TYPES, true);
+}
+
+
+/**
+ * Validate that outputs from one stage match inputs of another
+ */
+bool
+cross_validate_outputs_to_inputs(struct gl_shader_program *prog,
+ gl_shader *producer, gl_shader *consumer)
+{
+ glsl_symbol_table parameters;
+ /* FINISHME: Figure these out dynamically. */
+ const char *const producer_stage = "vertex";
+ const char *const consumer_stage = "fragment";
+
+ /* Find all shader outputs in the "producer" stage.
+ */
+ foreach_list(node, producer->ir) {
+ ir_variable *const var = ((ir_instruction *) node)->as_variable();
+
+ /* FINISHME: For geometry shaders, this should also look for inout
+ * FINISHME: variables.
+ */
+ if ((var == NULL) || (var->mode != ir_var_out))
+ continue;
+
+ parameters.add_variable(var);
+ }
+
+
+ /* Find all shader inputs in the "consumer" stage. Any variables that have
+ * matching outputs already in the symbol table must have the same type and
+ * qualifiers.
+ */
+ foreach_list(node, consumer->ir) {
+ ir_variable *const input = ((ir_instruction *) node)->as_variable();
+
+ /* FINISHME: For geometry shaders, this should also look for inout
+ * FINISHME: variables.
+ */
+ if ((input == NULL) || (input->mode != ir_var_in))
+ continue;
+
+ ir_variable *const output = parameters.get_variable(input->name);
+ if (output != NULL) {
+ /* Check that the types match between stages.
+ */
+ if (input->type != output->type) {
+ /* There is a bit of a special case for gl_TexCoord. This
+ * built-in is unsized by default. Appliations that variable
+ * access it must redeclare it with a size. There is some
+ * language in the GLSL spec that implies the fragment shader
+ * and vertex shader do not have to agree on this size. Other
+ * driver behave this way, and one or two applications seem to
+ * rely on it.
+ *
+ * Neither declaration needs to be modified here because the array
+ * sizes are fixed later when update_array_sizes is called.
+ *
+ * From page 48 (page 54 of the PDF) of the GLSL 1.10 spec:
+ *
+ * "Unlike user-defined varying variables, the built-in
+ * varying variables don't have a strict one-to-one
+ * correspondence between the vertex language and the
+ * fragment language."
+ */
+ if (!output->type->is_array()
+ || (strncmp("gl_", output->name, 3) != 0)) {
+ linker_error_printf(prog,
+ "%s shader output `%s' declared as "
+ "type `%s', but %s shader input declared "
+ "as type `%s'\n",
+ producer_stage, output->name,
+ output->type->name,
+ consumer_stage, input->type->name);
+ return false;
+ }
+ }
+
+ /* Check that all of the qualifiers match between stages.
+ */
+ if (input->centroid != output->centroid) {
+ linker_error_printf(prog,
+ "%s shader output `%s' %s centroid qualifier, "
+ "but %s shader input %s centroid qualifier\n",
+ producer_stage,
+ output->name,
+ (output->centroid) ? "has" : "lacks",
+ consumer_stage,
+ (input->centroid) ? "has" : "lacks");
+ return false;
+ }
+
+ if (input->invariant != output->invariant) {
+ linker_error_printf(prog,
+ "%s shader output `%s' %s invariant qualifier, "
+ "but %s shader input %s invariant qualifier\n",
+ producer_stage,
+ output->name,
+ (output->invariant) ? "has" : "lacks",
+ consumer_stage,
+ (input->invariant) ? "has" : "lacks");
+ return false;
+ }
+
+ if (input->interpolation != output->interpolation) {
+ linker_error_printf(prog,
+ "%s shader output `%s' specifies %s "
+ "interpolation qualifier, "
+ "but %s shader input specifies %s "
+ "interpolation qualifier\n",
+ producer_stage,
+ output->name,
+ output->interpolation_string(),
+ consumer_stage,
+ input->interpolation_string());
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+
+/**
+ * Populates a shaders symbol table with all global declarations
+ */
+static void
+populate_symbol_table(gl_shader *sh)
+{
+ sh->symbols = new(sh) glsl_symbol_table;
+
+ foreach_list(node, sh->ir) {
+ ir_instruction *const inst = (ir_instruction *) node;
+ ir_variable *var;
+ ir_function *func;
+
+ if ((func = inst->as_function()) != NULL) {
+ sh->symbols->add_function(func);
+ } else if ((var = inst->as_variable()) != NULL) {
+ sh->symbols->add_variable(var);
+ }
+ }
+}
+
+
+/**
+ * Remap variables referenced in an instruction tree
+ *
+ * This is used when instruction trees are cloned from one shader and placed in
+ * another. These trees will contain references to \c ir_variable nodes that
+ * do not exist in the target shader. This function finds these \c ir_variable
+ * references and replaces the references with matching variables in the target
+ * shader.
+ *
+ * If there is no matching variable in the target shader, a clone of the
+ * \c ir_variable is made and added to the target shader. The new variable is
+ * added to \b both the instruction stream and the symbol table.
+ *
+ * \param inst IR tree that is to be processed.
+ * \param symbols Symbol table containing global scope symbols in the
+ * linked shader.
+ * \param instructions Instruction stream where new variable declarations
+ * should be added.
+ */
+void
+remap_variables(ir_instruction *inst, struct gl_shader *target,
+ hash_table *temps)
+{
+ class remap_visitor : public ir_hierarchical_visitor {
+ public:
+ remap_visitor(struct gl_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->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(this->target, NULL);
+
+ this->symbols->add_variable(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(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 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.
+ *
+ * \param instructions Source instruction stream
+ * \param last Instruction after which new instructions should be
+ * inserted in the target instruction stream
+ * \param make_copies Flag selecting whether instructions in \c instructions
+ * should be copied (via \c ir_instruction::clone) into the
+ * target list or moved.
+ *
+ * \return
+ * The new "last" instruction in the target instruction stream. This pointer
+ * is suitable for use as the \c last parameter of a later call to this
+ * function.
+ */
+exec_node *
+move_non_declarations(exec_list *instructions, exec_node *last,
+ bool make_copies, gl_shader *target)
+{
+ 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_function())
+ continue;
+
+ ir_variable *var = inst->as_variable();
+ if ((var != NULL) && (var->mode != ir_var_temporary))
+ continue;
+
+ assert(inst->as_assignment()
+ || ((var != NULL) && (var->mode == ir_var_temporary)));
+
+ if (make_copies) {
+ inst = inst->clone(target, NULL);
+
+ if (var != NULL)
+ hash_table_insert(temps, inst, var);
+ else
+ remap_variables(inst, target, temps);
+ } else {
+ inst->remove();
+ }
+
+ last->insert_after(inst);
+ last = inst;
+ }
+
+ if (make_copies)
+ hash_table_dtor(temps);
+
+ return last;
+}
+
+/**
+ * Get the function signature for main from a shader
+ */
+static ir_function_signature *
+get_main_function_signature(gl_shader *sh)
+{
+ ir_function *const f = sh->symbols->get_function("main");
+ if (f != NULL) {
+ exec_list void_parameters;
+
+ /* Look for the 'void main()' signature and ensure that it's defined.
+ * This keeps the linker from accidentally pick a shader that just
+ * contains a prototype for main.
+ *
+ * We don't have to check for multiple definitions of main (in multiple
+ * shaders) because that would have already been caught above.
+ */
+ ir_function_signature *sig = f->matching_signature(&void_parameters);
+ if ((sig != NULL) && sig->is_defined) {
+ return sig;
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ * Combine a group of shaders for a single stage to generate a linked shader
+ *
+ * \note
+ * If this function is supplied a single shader, it is cloned, and the new
+ * shader is returned.
+ */
+static struct gl_shader *
+link_intrastage_shaders(void *mem_ctx,
+ struct gl_context *ctx,
+ struct gl_shader_program *prog,
+ struct gl_shader **shader_list,
+ unsigned num_shaders)
+{
+ /* Check that global variables defined in multiple shaders are consistent.
+ */
+ if (!cross_validate_globals(prog, shader_list, num_shaders, false))
+ return NULL;
+
+ /* Check that there is only a single definition of each function signature
+ * across all shaders.
+ */
+ for (unsigned i = 0; i < (num_shaders - 1); i++) {
+ foreach_list(node, shader_list[i]->ir) {
+ ir_function *const f = ((ir_instruction *) 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 the other shader has no function (and therefore no function
+ * signatures) with the same name, skip to the next shader.
+ */
+ if (other == NULL)
+ continue;
+
+ foreach_iter (exec_list_iterator, iter, *f) {
+ ir_function_signature *sig =
+ (ir_function_signature *) iter.get();
+
+ 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_builtin) {
+ linker_error_printf(prog,
+ "function `%s' is multiply defined",
+ f->name);
+ return NULL;
+ }
+ }
+ }
+ }
+ }
+
+ /* Find the shader that defines main, and make a clone of it.
+ *
+ * Starting with the clone, search for undefined references. If one is
+ * found, find the shader that defines it. Clone the reference and add
+ * it to the shader. Repeat until there are no undefined references or
+ * until a reference cannot be resolved.
+ */
+ gl_shader *main = NULL;
+ for (unsigned i = 0; i < num_shaders; i++) {
+ if (get_main_function_signature(shader_list[i]) != NULL) {
+ main = shader_list[i];
+ break;
+ }
+ }
+
+ if (main == NULL) {
+ linker_error_printf(prog, "%s shader lacks `main'\n",
+ (shader_list[0]->Type == GL_VERTEX_SHADER)
+ ? "vertex" : "fragment");
+ return NULL;
+ }
+
+ gl_shader *linked = ctx->Driver.NewShader(NULL, 0, main->Type);
+ linked->ir = new(linked) exec_list;
+ clone_ir_list(mem_ctx, linked->ir, main->ir);
+
+ populate_symbol_table(linked);
+
+ /* The a pointer to the main function in the final linked shader (i.e., the
+ * copy of the original shader that contained the main function).
+ */
+ ir_function_signature *const main_sig = get_main_function_signature(linked);
+
+ /* Move any instructions other than variable declarations or function
+ * declarations into main.
+ */
+ 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);
+ }
+
+ /* 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);
+
+ if (!link_function_calls(prog, linked, linking_shaders,
+ num_linking_shaders)) {
+ ctx->Driver.DeleteShader(ctx, linked);
+ linked = NULL;
+ }
+
+ free(linking_shaders);
+
+ /* Make a pass over all variable declarations to ensure that arrays with
+ * unspecified sizes have a size specified. The size is inferred from 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;
+
+ v.run(linked->ir);
+ }
+
+ return linked;
+}
+
+
+struct uniform_node {
+ exec_node link;
+ struct gl_uniform *u;
+ 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 < MESA_SHADER_TYPES; i++) {
+ if (prog->_LinkedShaders[i] == NULL)
+ continue;
+
+ 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 < MESA_SHADER_TYPES; j++) {
+ if (prog->_LinkedShaders[j] == NULL)
+ continue;
+
+ 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 = ralloc_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 = ralloc_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)
+{
+ /* */
+ exec_list uniforms;
+ unsigned total_uniforms = 0;
+ hash_table *ht = hash_table_ctor(32, hash_table_string_hash,
+ hash_table_string_compare);
+ void *mem_ctx = ralloc_context(NULL);
+
+ for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) {
+ if (prog->_LinkedShaders[i] == NULL)
+ continue;
+
+ unsigned next_position = 0;
+
+ foreach_list(node, prog->_LinkedShaders[i]->ir) {
+ ir_variable *const var = ((ir_instruction *) node)->as_variable();
+
+ if ((var == NULL) || (var->mode != ir_var_uniform))
+ continue;
+
+ 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;
+ }
+
+ var->location = next_position;
+ add_uniform(mem_ctx, &uniforms, ht, var->name, var->type,
+ prog->_LinkedShaders[i]->Type,
+ &next_position, &total_uniforms);
+ }
+ }
+
+ ralloc_free(mem_ctx);
+
+ gl_uniform_list *ul = (gl_uniform_list *)
+ calloc(1, sizeof(gl_uniform_list));
+
+ ul->Size = total_uniforms;
+ ul->NumUniforms = total_uniforms;
+ ul->Uniforms = (gl_uniform *) calloc(total_uniforms, sizeof(gl_uniform));
+
+ unsigned idx = 0;
+ uniform_node *next;
+ for (uniform_node *node = (uniform_node *) uniforms.head
+ ; node->link.next != NULL
+ ; node = next) {
+ next = (uniform_node *) node->link.next;
+
+ node->link.remove();
+ memcpy(&ul->Uniforms[idx], node->u, sizeof(gl_uniform));
+ idx++;
+
+ free(node->u);
+ free(node);
+ }
+
+ hash_table_dtor(ht);
+
+ prog->Uniforms = ul;
+}
+
+
+/**
+ * Find a contiguous set of available bits in a bitmask
+ *
+ * \param used_mask Bits representing used (1) and unused (0) locations
+ * \param needed_count Number of contiguous bits needed.
+ *
+ * \return
+ * Base location of the available bits on success or -1 on failure.
+ */
+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;
+
+ /* The comparison to 32 is redundant, but without it GCC emits "warning:
+ * cannot optimize possibly infinite loops" for the loop below.
+ */
+ 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;
+}
+
+
+bool
+assign_attribute_locations(gl_shader_program *prog, unsigned max_attribute_index)
+{
+ /* Mark invalid attribute locations as being used.
+ */
+ unsigned used_locations = (max_attribute_index >= 32)
+ ? ~0 : ~((1 << max_attribute_index) - 1);
+
+ gl_shader *const sh = prog->_LinkedShaders[0];
+ assert(sh->Type == GL_VERTEX_SHADER);
+
+ /* Operate in a total of four passes.
+ *
+ * 1. Invalidate the location assignments for all vertex shader inputs.
+ *
+ * 2. Assign locations for inputs that have user-defined (via
+ * glBindVertexAttribLocation) locatoins.
+ *
+ * 3. Sort the attributes without assigned locations by number of slots
+ * required in decreasing order. Fragmentation caused by attribute
+ * locations assigned by the application may prevent large attributes
+ * from having enough contiguous space.
+ *
+ * 4. Assign locations to any inputs without assigned locations.
+ */
+
+ invalidate_variable_locations(sh, ir_var_in, VERT_ATTRIB_GENERIC0);
+
+ if (prog->Attributes != NULL) {
+ for (unsigned i = 0; i < prog->Attributes->NumParameters; i++) {
+ ir_variable *const var =
+ sh->symbols->get_variable(prog->Attributes->Parameters[i].Name);
+
+ /* Note: attributes that occupy multiple slots, such as arrays or
+ * matrices, may appear in the attrib array multiple times.
+ */
+ if ((var == NULL) || (var->location != -1))
+ continue;
+
+ /* From page 61 of the OpenGL 4.0 spec:
+ *
+ * "LinkProgram will fail if the attribute bindings assigned by
+ * BindAttribLocation do not leave not enough space to assign a
+ * location for an active matrix attribute or an active attribute
+ * array, both of which require multiple contiguous generic
+ * attributes."
+ *
+ * Previous versions of the spec contain similar language but omit the
+ * bit about attribute arrays.
+ *
+ * Page 61 of the OpenGL 4.0 spec also says:
+ *
+ * "It is possible for an application to bind more than one
+ * attribute name to the same location. This is referred to as
+ * aliasing. This will only work if only one of the aliased
+ * attributes is active in the executable program, or if no path
+ * through the shader consumes more than one attribute of a set
+ * of attributes aliased to the same location. A link error can
+ * occur if the linker determines that every path through the
+ * shader consumes multiple aliased attributes, but
+ * implementations are not required to generate an error in this
+ * case."
+ *
+ * These two paragraphs are either somewhat contradictory, or I don't
+ * fully understand one or both of them.
+ */
+ /* FINISHME: The code as currently written does not support attribute
+ * FINISHME: location aliasing (see comment above).
+ */
+ const int attr = prog->Attributes->Parameters[i].StateIndexes[0];
+ const unsigned slots = count_attribute_slots(var->type);
+
+ /* Mask representing the contiguous slots that will be used by this
+ * attribute.
+ */
+ const unsigned use_mask = (1 << slots) - 1;
+
+ /* Generate a link error if the set of bits requested for this
+ * attribute overlaps any previously allocated bits.
+ */
+ if ((~(use_mask << attr) & used_locations) != used_locations) {
+ linker_error_printf(prog,
+ "insufficient contiguous attribute locations "
+ "available for vertex shader input `%s'",
+ var->name);
+ return false;
+ }
+
+ var->location = VERT_ATTRIB_GENERIC0 + attr;
+ used_locations |= (use_mask << attr);
+ }
+ }
+
+ /* Temporary storage for the set of attributes that need locations assigned.
+ */
+ struct temp_attr {
+ unsigned slots;
+ ir_variable *var;
+
+ /* Used below in the call to qsort. */
+ 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;
+
+ /* Reversed because we want a descending order sort below. */
+ return r->slots - l->slots;
+ }
+ } to_assign[16];
+
+ unsigned num_attr = 0;
+
+ foreach_list(node, sh->ir) {
+ ir_variable *const var = ((ir_instruction *) node)->as_variable();
+
+ 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)
+ continue;
+
+ to_assign[num_attr].slots = count_attribute_slots(var->type);
+ to_assign[num_attr].var = var;
+ num_attr++;
+ }
+
+ /* If all of the attributes were assigned locations by the application (or
+ * are built-in attributes with fixed locations), return early. This should
+ * be the common case.
+ */
+ if (num_attr == 0)
+ return true;
+
+ qsort(to_assign, num_attr, sizeof(to_assign[0]), temp_attr::compare);
+
+ /* VERT_ATTRIB_GENERIC0 is a psdueo-alias for VERT_ATTRIB_POS. It can only
+ * be explicitly assigned by via glBindAttribLocation. Mark it as reserved
+ * to prevent it from being automatically allocated below.
+ */
+ 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
+ * attribute.
+ */
+ const unsigned use_mask = (1 << to_assign[i].slots) - 1;
+
+ int location = find_available_slots(used_locations, to_assign[i].slots);
+
+ if (location < 0) {
+ linker_error_printf(prog,
+ "insufficient contiguous attribute locations "
+ "available for vertex shader input `%s'",
+ to_assign[i].var->name);
+ return false;
+ }
+
+ to_assign[i].var->location = VERT_ATTRIB_GENERIC0 + location;
+ used_locations |= (use_mask << location);
+ }
+
+ return true;
+}
+
+
+/**
+ * Demote shader inputs and outputs that are not used in other stages
+ */
+void
+demote_shader_inputs_and_outputs(gl_shader *sh, enum ir_variable_mode mode)
+{
+ foreach_list(node, sh->ir) {
+ ir_variable *const var = ((ir_instruction *) node)->as_variable();
+
+ if ((var == NULL) || (var->mode != int(mode)))
+ continue;
+
+ /* A shader 'in' or 'out' variable is only really an input or output if
+ * its value is used by other shader stages. This will cause the variable
+ * to have a location assigned.
+ */
+ 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;
+ unsigned input_index = FRAG_ATTRIB_VAR0;
+
+ /* Operate in a total of three passes.
+ *
+ * 1. Assign locations for any matching inputs and outputs.
+ *
+ * 2. Mark output variables in the producer that do not have locations as
+ * not being outputs. This lets the optimizer eliminate them.
+ *
+ * 3. Mark input variables in the consumer that do not have locations as
+ * not being inputs. This lets the optimizer eliminate them.
+ */
+
+ invalidate_variable_locations(producer, ir_var_out, VERT_RESULT_VAR0);
+ invalidate_variable_locations(consumer, ir_var_in, FRAG_ATTRIB_VAR0);
+
+ foreach_list(node, producer->ir) {
+ ir_variable *const output_var = ((ir_instruction *) node)->as_variable();
+
+ if ((output_var == NULL) || (output_var->mode != ir_var_out)
+ || (output_var->location != -1))
+ continue;
+
+ ir_variable *const input_var =
+ consumer->symbols->get_variable(output_var->name);
+
+ if ((input_var == NULL) || (input_var->mode != ir_var_in))
+ continue;
+
+ assert(input_var->location == -1);
+
+ output_var->location = output_index;
+ input_var->location = input_index;
+
+ /* FINISHME: Support for "varying" records in GLSL 1.50. */
+ assert(!output_var->type->is_record());
+
+ if (output_var->type->is_array()) {
+ const unsigned slots = output_var->type->length
+ * output_var->type->fields.array->matrix_columns;
+
+ output_index += slots;
+ input_index += slots;
+ } else {
+ const unsigned slots = output_var->type->matrix_columns;
+
+ output_index += slots;
+ input_index += slots;
+ }
+ }
+
+ foreach_list(node, consumer->ir) {
+ ir_variable *const var = ((ir_instruction *) node)->as_variable();
+
+ if ((var == NULL) || (var->mode != ir_var_in))
+ continue;
+
+ 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_context *ctx, struct gl_shader_program *prog)
+{
+ void *mem_ctx = ralloc_context(NULL); // temporary linker context
+
+ prog->LinkStatus = false;
+ prog->Validated = false;
+ prog->_Used = false;
+
+ if (prog->InfoLog != NULL)
+ ralloc_free(prog->InfoLog);
+
+ prog->InfoLog = ralloc_strdup(NULL, "");
+
+ /* Separate the shaders into groups based on their type.
+ */
+ struct gl_shader **vert_shader_list;
+ unsigned num_vert_shaders = 0;
+ struct gl_shader **frag_shader_list;
+ unsigned num_frag_shaders = 0;
+
+ vert_shader_list = (struct gl_shader **)
+ 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];
+ num_vert_shaders++;
+ break;
+ case GL_FRAGMENT_SHADER:
+ frag_shader_list[num_frag_shaders] = prog->Shaders[i];
+ num_frag_shaders++;
+ break;
+ case GL_GEOMETRY_SHADER:
+ /* FINISHME: Support geometry shaders. */
+ assert(prog->Shaders[i]->Type != GL_GEOMETRY_SHADER);
+ break;
+ }
+ }
+
+ /* 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 < MESA_SHADER_TYPES; i++) {
+ if (prog->_LinkedShaders[i] != NULL)
+ ctx->Driver.DeleteShader(ctx, prog->_LinkedShaders[i]);
+
+ prog->_LinkedShaders[i] = NULL;
+ }
+
+ /* Link all shaders for a particular stage and validate the result.
+ */
+ if (num_vert_shaders > 0) {
+ gl_shader *const sh =
+ link_intrastage_shaders(mem_ctx, ctx, prog, vert_shader_list,
+ num_vert_shaders);
+
+ if (sh == NULL)
+ goto done;
+
+ if (!validate_vertex_shader_executable(prog, sh))
+ goto done;
+
+ _mesa_reference_shader(ctx, &prog->_LinkedShaders[MESA_SHADER_VERTEX],
+ sh);
+ }
+
+ if (num_frag_shaders > 0) {
+ gl_shader *const sh =
+ link_intrastage_shaders(mem_ctx, ctx, prog, frag_shader_list,
+ num_frag_shaders);
+
+ if (sh == NULL)
+ goto done;
+
+ if (!validate_fragment_shader_executable(prog, sh))
+ goto done;
+
+ _mesa_reference_shader(ctx, &prog->_LinkedShaders[MESA_SHADER_FRAGMENT],
+ sh);
+ }
+
+ /* Here begins the inter-stage linking phase. Some initial validation is
+ * performed, then locations are assigned for uniforms, attributes, and
+ * varyings.
+ */
+ if (cross_validate_uniforms(prog)) {
+ unsigned prev;
+
+ for (prev = 0; prev < MESA_SHADER_TYPES; prev++) {
+ if (prog->_LinkedShaders[prev] != NULL)
+ break;
+ }
+
+ /* Validate the inputs of each stage with the output of the preceeding
+ * stage.
+ */
+ for (unsigned i = prev + 1; i < MESA_SHADER_TYPES; i++) {
+ if (prog->_LinkedShaders[i] == NULL)
+ continue;
+
+ if (!cross_validate_outputs_to_inputs(prog,
+ prog->_LinkedShaders[prev],
+ prog->_LinkedShaders[i]))
+ goto done;
+
+ prev = i;
+ }
+
+ prog->LinkStatus = true;
+ }
+
+ /* 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 < MESA_SHADER_TYPES; i++) {
+ if (prog->_LinkedShaders[i] == NULL)
+ continue;
+
+ while (do_common_optimization(prog->_LinkedShaders[i]->ir, true, 32))
+ ;
+ }
+
+ update_array_sizes(prog);
+
+ assign_uniform_locations(prog);
+
+ if (prog->_LinkedShaders[MESA_SHADER_VERTEX] != NULL) {
+ /* 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)) {
+ prog->LinkStatus = false;
+ goto done;
+ }
+ }
+
+ unsigned prev;
+ for (prev = 0; prev < MESA_SHADER_TYPES; prev++) {
+ if (prog->_LinkedShaders[prev] != NULL)
+ break;
+ }
+
+ for (unsigned i = prev + 1; i < MESA_SHADER_TYPES; i++) {
+ if (prog->_LinkedShaders[i] == NULL)
+ continue;
+
+ assign_varying_locations(prog,
+ prog->_LinkedShaders[prev],
+ prog->_LinkedShaders[i]);
+ prev = i;
+ }
+
+ if (prog->_LinkedShaders[MESA_SHADER_VERTEX] != NULL) {
+ demote_shader_inputs_and_outputs(prog->_LinkedShaders[MESA_SHADER_VERTEX],
+ ir_var_out);
+ }
+
+ if (prog->_LinkedShaders[MESA_SHADER_GEOMETRY] != NULL) {
+ gl_shader *const sh = prog->_LinkedShaders[MESA_SHADER_GEOMETRY];
+
+ demote_shader_inputs_and_outputs(sh, ir_var_in);
+ demote_shader_inputs_and_outputs(sh, ir_var_inout);
+ demote_shader_inputs_and_outputs(sh, ir_var_out);
+ }
+
+ if (prog->_LinkedShaders[MESA_SHADER_FRAGMENT] != NULL) {
+ gl_shader *const sh = prog->_LinkedShaders[MESA_SHADER_FRAGMENT];
+
+ demote_shader_inputs_and_outputs(sh, ir_var_in);
+ }
+
+ /* OpenGL ES requires that a vertex shader and a fragment shader both be
+ * present in a linked program. By checking for use of shading language
+ * version 1.00, we also catch the GL_ARB_ES2_compatibility case.
+ */
+ if (ctx->API == API_OPENGLES2 || prog->Version == 100) {
+ if (prog->_LinkedShaders[MESA_SHADER_VERTEX] == NULL) {
+ linker_error_printf(prog, "program lacks a vertex shader\n");
+ prog->LinkStatus = false;
+ } else if (prog->_LinkedShaders[MESA_SHADER_FRAGMENT] == NULL) {
+ linker_error_printf(prog, "program lacks a fragment shader\n");
+ prog->LinkStatus = false;
+ }
+ }
+
+ /* FINISHME: Assign fragment shader output locations. */
+
+done:
+ free(vert_shader_list);
+
+ for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) {
+ if (prog->_LinkedShaders[i] == NULL)
+ continue;
+
+ /* Retain any live IR, but trash the rest. */
+ reparent_ir(prog->_LinkedShaders[i]->ir, prog->_LinkedShaders[i]->ir);
+ }
+
+ ralloc_free(mem_ctx);
+}
diff --git a/mesalib/src/mesa/main/mtypes.h b/mesalib/src/mesa/main/mtypes.h
index 020595bd0..714d63afc 100644
--- a/mesalib/src/mesa/main/mtypes.h
+++ b/mesalib/src/mesa/main/mtypes.h
@@ -1240,7 +1240,7 @@ struct gl_texture_image
* GL_DEPTH_COMPONENT or GL_DEPTH_STENCIL_EXT
* only. Used for choosing TexEnv arithmetic.
*/
- GLuint TexFormat; /**< The actual format: MESA_FORMAT_x */
+ gl_format TexFormat; /**< The actual texture memory format */
GLuint Border; /**< 0 or 1 */
GLuint Width; /**< = 2^WidthLog2 + 2*Border */
@@ -1643,6 +1643,7 @@ struct gl_array_attrib
GLuint RestartIndex;
GLbitfield NewState; /**< mask of _NEW_ARRAY_* values */
+ GLboolean RebindArrays; /**< whether the VBO module should rebind arrays */
/* GL_ARB_vertex_buffer_object */
struct gl_buffer_object *ArrayBufferObj;
@@ -2379,7 +2380,7 @@ struct gl_renderbuffer
GLenum InternalFormat; /**< The user-specified format */
GLenum _BaseFormat; /**< Either GL_RGB, GL_RGBA, GL_DEPTH_COMPONENT or
GL_STENCIL_INDEX. */
- GLuint Format; /**< The actual format: MESA_FORMAT_x */
+ gl_format Format; /**< The actual renderbuffer memory format */
GLubyte NumSamples;
diff --git a/mesalib/src/mesa/main/state.c b/mesalib/src/mesa/main/state.c
index c07e2c380..f11578faf 100644
--- a/mesalib/src/mesa/main/state.c
+++ b/mesalib/src/mesa/main/state.c
@@ -662,6 +662,8 @@ _mesa_update_state_locked( struct gl_context *ctx )
ctx->NewState = 0;
ctx->Driver.UpdateState(ctx, new_state);
ctx->Array.NewState = 0;
+ if (!ctx->Array.RebindArrays)
+ ctx->Array.RebindArrays = (new_state & (_NEW_ARRAY | _NEW_PROGRAM)) != 0;
}
diff --git a/mesalib/src/mesa/state_tracker/st_atom_blend.c b/mesalib/src/mesa/state_tracker/st_atom_blend.c
index 26bb3dab9..e3b3b9936 100644
--- a/mesalib/src/mesa/state_tracker/st_atom_blend.c
+++ b/mesalib/src/mesa/state_tracker/st_atom_blend.c
@@ -191,7 +191,7 @@ update_blend( struct st_context *st )
{
struct pipe_blend_state *blend = &st->state.blend;
unsigned num_state = 1;
- unsigned i;
+ unsigned i, j;
memset(blend, 0, sizeof(*blend));
@@ -214,12 +214,15 @@ update_blend( struct st_context *st )
}
else if (st->ctx->Color.BlendEnabled) {
/* blending enabled */
- for (i = 0; i < num_state; i++) {
+ for (i = 0, j = 0; i < num_state; i++) {
blend->rt[i].blend_enable = (st->ctx->Color.BlendEnabled >> i) & 0x1;
+ if (st->ctx->Extensions.ARB_draw_buffers_blend)
+ j = i;
+
blend->rt[i].rgb_func =
- translate_blend(st->ctx->Color.Blend[i].EquationRGB);
+ translate_blend(st->ctx->Color.Blend[j].EquationRGB);
if (st->ctx->Color.Blend[i].EquationRGB == GL_MIN ||
st->ctx->Color.Blend[i].EquationRGB == GL_MAX) {
@@ -229,13 +232,13 @@ update_blend( struct st_context *st )
}
else {
blend->rt[i].rgb_src_factor =
- translate_blend(st->ctx->Color.Blend[i].SrcRGB);
+ translate_blend(st->ctx->Color.Blend[j].SrcRGB);
blend->rt[i].rgb_dst_factor =
- translate_blend(st->ctx->Color.Blend[i].DstRGB);
+ translate_blend(st->ctx->Color.Blend[j].DstRGB);
}
blend->rt[i].alpha_func =
- translate_blend(st->ctx->Color.Blend[i].EquationA);
+ translate_blend(st->ctx->Color.Blend[j].EquationA);
if (st->ctx->Color.Blend[i].EquationA == GL_MIN ||
st->ctx->Color.Blend[i].EquationA == GL_MAX) {
@@ -245,9 +248,9 @@ update_blend( struct st_context *st )
}
else {
blend->rt[i].alpha_src_factor =
- translate_blend(st->ctx->Color.Blend[i].SrcA);
+ translate_blend(st->ctx->Color.Blend[j].SrcA);
blend->rt[i].alpha_dst_factor =
- translate_blend(st->ctx->Color.Blend[i].DstA);
+ translate_blend(st->ctx->Color.Blend[j].DstA);
}
}
}
diff --git a/mesalib/src/mesa/state_tracker/st_cb_bitmap.c b/mesalib/src/mesa/state_tracker/st_cb_bitmap.c
index ddd130a81..51623c179 100644
--- a/mesalib/src/mesa/state_tracker/st_cb_bitmap.c
+++ b/mesalib/src/mesa/state_tracker/st_cb_bitmap.c
@@ -349,6 +349,7 @@ setup_bitmap_vertex_data(struct st_context *st, bool normalized,
if (!st->bitmap.vbuf) {
st->bitmap.vbuf = pipe_buffer_create(pipe->screen,
PIPE_BIND_VERTEX_BUFFER,
+ PIPE_USAGE_STREAM,
max_slots *
sizeof(st->bitmap.vertices));
}
@@ -456,6 +457,7 @@ draw_bitmap_quad(struct gl_context *ctx, GLint x, GLint y, GLfloat z,
cso_save_fragment_shader(cso);
cso_save_vertex_shader(cso);
cso_save_vertex_elements(cso);
+ cso_save_vertex_buffers(cso);
/* rasterizer state: just scissor */
st->bitmap.rasterizer.scissor = ctx->Scissor.Enabled;
@@ -517,7 +519,7 @@ draw_bitmap_quad(struct gl_context *ctx, GLint x, GLint y, GLfloat z,
sv->texture->target != PIPE_TEXTURE_RECT,
x, y, width, height, z, color);
- util_draw_vertex_buffer(pipe, st->bitmap.vbuf, offset,
+ util_draw_vertex_buffer(pipe, st->cso_context, st->bitmap.vbuf, offset,
PIPE_PRIM_TRIANGLE_FAN,
4, /* verts */
3); /* attribs/vert */
@@ -531,6 +533,7 @@ draw_bitmap_quad(struct gl_context *ctx, GLint x, GLint y, GLfloat z,
cso_restore_fragment_shader(cso);
cso_restore_vertex_shader(cso);
cso_restore_vertex_elements(cso);
+ cso_restore_vertex_buffers(cso);
}
diff --git a/mesalib/src/mesa/state_tracker/st_cb_bufferobjects.c b/mesalib/src/mesa/state_tracker/st_cb_bufferobjects.c
index ba8a8cf89..9dd1f8a3e 100644
--- a/mesalib/src/mesa/state_tracker/st_cb_bufferobjects.c
+++ b/mesalib/src/mesa/state_tracker/st_cb_bufferobjects.c
@@ -171,7 +171,7 @@ st_bufferobj_data(struct gl_context *ctx,
struct st_context *st = st_context(ctx);
struct pipe_context *pipe = st->pipe;
struct st_buffer_object *st_obj = st_buffer_object(obj);
- unsigned buffer_usage;
+ unsigned bind, pipe_usage;
st_obj->Base.Size = size;
st_obj->Base.Usage = usage;
@@ -179,22 +179,43 @@ st_bufferobj_data(struct gl_context *ctx,
switch(target) {
case GL_PIXEL_PACK_BUFFER_ARB:
case GL_PIXEL_UNPACK_BUFFER_ARB:
- buffer_usage = PIPE_BIND_RENDER_TARGET;
+ bind = PIPE_BIND_RENDER_TARGET | PIPE_BIND_SAMPLER_VIEW;
break;
case GL_ARRAY_BUFFER_ARB:
- buffer_usage = PIPE_BIND_VERTEX_BUFFER;
+ bind = PIPE_BIND_VERTEX_BUFFER;
break;
case GL_ELEMENT_ARRAY_BUFFER_ARB:
- buffer_usage = PIPE_BIND_INDEX_BUFFER;
+ bind = PIPE_BIND_INDEX_BUFFER;
break;
default:
- buffer_usage = 0;
+ bind = 0;
+ }
+
+ switch (usage) {
+ case GL_STATIC_DRAW:
+ case GL_STATIC_READ:
+ case GL_STATIC_COPY:
+ pipe_usage = PIPE_USAGE_STATIC;
+ break;
+ case GL_DYNAMIC_DRAW:
+ case GL_DYNAMIC_READ:
+ case GL_DYNAMIC_COPY:
+ pipe_usage = PIPE_USAGE_DYNAMIC;
+ break;
+ case GL_STREAM_DRAW:
+ case GL_STREAM_READ:
+ case GL_STREAM_COPY:
+ pipe_usage = PIPE_USAGE_STREAM;
+ break;
+ default:
+ pipe_usage = PIPE_USAGE_DEFAULT;
}
pipe_resource_reference( &st_obj->buffer, NULL );
if (size != 0) {
- st_obj->buffer = pipe_buffer_create(pipe->screen, buffer_usage, size);
+ st_obj->buffer = pipe_buffer_create(pipe->screen, bind,
+ pipe_usage, size);
if (!st_obj->buffer) {
return GL_FALSE;
diff --git a/mesalib/src/mesa/state_tracker/st_cb_clear.c b/mesalib/src/mesa/state_tracker/st_cb_clear.c
index 3e27be271..461f4e315 100644
--- a/mesalib/src/mesa/state_tracker/st_cb_clear.c
+++ b/mesalib/src/mesa/state_tracker/st_cb_clear.c
@@ -138,6 +138,7 @@ draw_quad(struct st_context *st,
if (!st->clear.vbuf) {
st->clear.vbuf = pipe_buffer_create(pipe->screen,
PIPE_BIND_VERTEX_BUFFER,
+ PIPE_USAGE_STREAM,
max_slots * sizeof(st->clear.vertices));
}
@@ -172,7 +173,8 @@ draw_quad(struct st_context *st,
st->clear.vertices);
/* draw */
- util_draw_vertex_buffer(pipe,
+ util_draw_vertex_buffer(pipe,
+ st->cso_context,
st->clear.vbuf,
st->clear.vbuf_slot * sizeof(st->clear.vertices),
PIPE_PRIM_TRIANGLE_FAN,
@@ -221,6 +223,7 @@ clear_with_quad(struct gl_context *ctx,
cso_save_fragment_shader(st->cso_context);
cso_save_vertex_shader(st->cso_context);
cso_save_vertex_elements(st->cso_context);
+ cso_save_vertex_buffers(st->cso_context);
/* blend state: RGBA masking */
{
@@ -309,6 +312,7 @@ clear_with_quad(struct gl_context *ctx,
cso_restore_fragment_shader(st->cso_context);
cso_restore_vertex_shader(st->cso_context);
cso_restore_vertex_elements(st->cso_context);
+ cso_restore_vertex_buffers(st->cso_context);
}
diff --git a/mesalib/src/mesa/state_tracker/st_cb_drawpixels.c b/mesalib/src/mesa/state_tracker/st_cb_drawpixels.c
index 56c7e8581..18ac6c8e0 100644
--- a/mesalib/src/mesa/state_tracker/st_cb_drawpixels.c
+++ b/mesalib/src/mesa/state_tracker/st_cb_drawpixels.c
@@ -522,10 +522,11 @@ draw_quad(struct gl_context *ctx, GLfloat x0, GLfloat y0, GLfloat z,
/* allocate/load buffer object with vertex data */
buf = pipe_buffer_create(pipe->screen,
PIPE_BIND_VERTEX_BUFFER,
+ PIPE_USAGE_STATIC,
sizeof(verts));
pipe_buffer_write(st->pipe, buf, 0, sizeof(verts), verts);
- util_draw_vertex_buffer(pipe, buf, 0,
+ util_draw_vertex_buffer(pipe, st->cso_context, buf, 0,
PIPE_PRIM_QUADS,
4, /* verts */
3); /* attribs/vert */
@@ -570,6 +571,7 @@ draw_textured_quad(struct gl_context *ctx, GLint x, GLint y, GLfloat z,
cso_save_fragment_shader(cso);
cso_save_vertex_shader(cso);
cso_save_vertex_elements(cso);
+ cso_save_vertex_buffers(cso);
if (write_stencil) {
cso_save_depth_stencil_alpha(cso);
cso_save_blend(cso);
@@ -686,6 +688,7 @@ draw_textured_quad(struct gl_context *ctx, GLint x, GLint y, GLfloat z,
cso_restore_fragment_shader(cso);
cso_restore_vertex_shader(cso);
cso_restore_vertex_elements(cso);
+ cso_restore_vertex_buffers(cso);
if (write_stencil) {
cso_restore_depth_stencil_alpha(cso);
cso_restore_blend(cso);
diff --git a/mesalib/src/mesa/state_tracker/st_cb_drawtex.c b/mesalib/src/mesa/state_tracker/st_cb_drawtex.c
index 5976f1048..db299eb8d 100644
--- a/mesalib/src/mesa/state_tracker/st_cb_drawtex.c
+++ b/mesalib/src/mesa/state_tracker/st_cb_drawtex.c
@@ -139,6 +139,7 @@ st_DrawTex(struct gl_context *ctx, GLfloat x, GLfloat y, GLfloat z,
/* create the vertex buffer */
vbuffer = pipe_buffer_create(pipe->screen, PIPE_BIND_VERTEX_BUFFER,
+ PIPE_USAGE_STREAM,
numAttribs * 4 * 4 * sizeof(GLfloat));
/* load vertex buffer */
@@ -230,6 +231,7 @@ st_DrawTex(struct gl_context *ctx, GLfloat x, GLfloat y, GLfloat z,
cso_save_viewport(cso);
cso_save_vertex_shader(cso);
cso_save_vertex_elements(cso);
+ cso_save_vertex_buffers(cso);
{
void *vs = lookup_shader(pipe, numAttribs,
@@ -264,7 +266,7 @@ st_DrawTex(struct gl_context *ctx, GLfloat x, GLfloat y, GLfloat z,
}
- util_draw_vertex_buffer(pipe, vbuffer,
+ util_draw_vertex_buffer(pipe, cso, vbuffer,
0, /* offset */
PIPE_PRIM_TRIANGLE_FAN,
4, /* verts */
@@ -277,6 +279,7 @@ st_DrawTex(struct gl_context *ctx, GLfloat x, GLfloat y, GLfloat z,
cso_restore_viewport(cso);
cso_restore_vertex_shader(cso);
cso_restore_vertex_elements(cso);
+ cso_restore_vertex_buffers(cso);
}
diff --git a/mesalib/src/mesa/state_tracker/st_context.c b/mesalib/src/mesa/state_tracker/st_context.c
index b16d965f8..8f3a7415d 100644
--- a/mesalib/src/mesa/state_tracker/st_context.c
+++ b/mesalib/src/mesa/state_tracker/st_context.c
@@ -203,6 +203,11 @@ static void st_destroy_context_priv( struct st_context *st )
st_destroy_drawpix(st);
st_destroy_drawtex(st);
+ /* Unreference any user vertex buffers. */
+ for (i = 0; i < st->num_user_vbs; i++) {
+ pipe_resource_reference(&st->user_vb[i], NULL);
+ }
+
for (i = 0; i < Elements(st->state.sampler_views); i++) {
pipe_sampler_view_reference(&st->state.sampler_views[i], NULL);
}
diff --git a/mesalib/src/mesa/state_tracker/st_context.h b/mesalib/src/mesa/state_tracker/st_context.h
index 492ee600e..ef54fd7cd 100644
--- a/mesalib/src/mesa/state_tracker/st_context.h
+++ b/mesalib/src/mesa/state_tracker/st_context.h
@@ -185,6 +185,11 @@ struct st_context
int force_msaa;
void *winsys_drawable_handle;
+
+ /* User vertex buffers. */
+ struct pipe_resource *user_vb[PIPE_MAX_ATTRIBS];
+ unsigned user_vb_stride[PIPE_MAX_ATTRIBS];
+ unsigned num_user_vbs;
};
diff --git a/mesalib/src/mesa/state_tracker/st_draw.c b/mesalib/src/mesa/state_tracker/st_draw.c
index 19466ea44..a31cbdc9e 100644
--- a/mesalib/src/mesa/state_tracker/st_draw.c
+++ b/mesalib/src/mesa/state_tracker/st_draw.c
@@ -243,13 +243,11 @@ st_pipe_vertex_format(GLenum type, GLuint size, GLenum format,
static GLboolean
is_interleaved_arrays(const struct st_vertex_program *vp,
const struct st_vp_variant *vpv,
- const struct gl_client_array **arrays,
- GLboolean *userSpace)
+ const struct gl_client_array **arrays)
{
GLuint attr;
const struct gl_buffer_object *firstBufObj = NULL;
GLint firstStride = -1;
- GLuint num_client_arrays = 0;
const GLubyte *client_addr = NULL;
for (attr = 0; attr < vpv->num_inputs; attr++) {
@@ -263,9 +261,8 @@ is_interleaved_arrays(const struct st_vertex_program *vp,
else if (firstStride != stride) {
return GL_FALSE;
}
-
+
if (!bufObj || !bufObj->Name) {
- num_client_arrays++;
/* Try to detect if the client-space arrays are
* "close" to each other.
*/
@@ -285,57 +282,11 @@ is_interleaved_arrays(const struct st_vertex_program *vp,
}
}
- *userSpace = (num_client_arrays == vpv->num_inputs);
- /* debug_printf("user space: %s (%d arrays, %d inputs)\n",
- (int)*userSpace ? "Yes" : "No", num_client_arrays, vp->num_inputs); */
-
return GL_TRUE;
}
/**
- * Compute the memory range occupied by the arrays.
- */
-static void
-get_arrays_bounds(const struct st_vertex_program *vp,
- const struct st_vp_variant *vpv,
- const struct gl_client_array **arrays,
- GLuint max_index,
- const GLubyte **low, const GLubyte **high)
-{
- const GLubyte *low_addr = NULL;
- const GLubyte *high_addr = NULL;
- GLuint attr;
-
- /* debug_printf("get_arrays_bounds: Handling %u attrs\n", vpv->num_inputs); */
-
- for (attr = 0; attr < vpv->num_inputs; attr++) {
- const GLuint mesaAttr = vp->index_to_input[attr];
- const GLint stride = arrays[mesaAttr]->StrideB;
- const GLubyte *start = arrays[mesaAttr]->Ptr;
- const unsigned sz = (arrays[mesaAttr]->Size *
- _mesa_sizeof_type(arrays[mesaAttr]->Type));
- const GLubyte *end = start + (max_index * stride) + sz;
-
- /* debug_printf("attr %u: stride %d size %u start %p end %p\n",
- attr, stride, sz, start, end); */
-
- if (attr == 0) {
- low_addr = start;
- high_addr = end;
- }
- else {
- low_addr = MIN2(low_addr, start);
- high_addr = MAX2(high_addr, end);
- }
- }
-
- *low = low_addr;
- *high = high_addr;
-}
-
-
-/**
* Set up for drawing interleaved arrays that all live in one VBO
* or all live in user space.
* \param vbuffer returns vertex buffer info
@@ -346,15 +297,21 @@ setup_interleaved_attribs(struct gl_context *ctx,
const struct st_vertex_program *vp,
const struct st_vp_variant *vpv,
const struct gl_client_array **arrays,
- GLuint max_index,
- GLboolean userSpace,
struct pipe_vertex_buffer *vbuffer,
- struct pipe_vertex_element velements[])
+ struct pipe_vertex_element velements[],
+ unsigned max_index)
{
struct st_context *st = st_context(ctx);
struct pipe_context *pipe = st->pipe;
GLuint attr;
- const GLubyte *offset0 = NULL;
+ const GLubyte *low_addr = NULL;
+
+ /* Find the lowest address. */
+ for (attr = 0; attr < vpv->num_inputs; attr++) {
+ const GLubyte *start = arrays[vp->index_to_input[attr]]->Ptr;
+
+ low_addr = !low_addr ? start : MIN2(low_addr, start);
+ }
for (attr = 0; attr < vpv->num_inputs; attr++) {
const GLuint mesaAttr = vp->index_to_input[attr];
@@ -362,40 +319,28 @@ setup_interleaved_attribs(struct gl_context *ctx,
struct st_buffer_object *stobj = st_buffer_object(bufobj);
GLsizei stride = arrays[mesaAttr]->StrideB;
- /*printf("stobj %u = %p\n", attr, (void*)stobj);*/
-
if (attr == 0) {
- const GLubyte *low, *high;
-
- get_arrays_bounds(vp, vpv, arrays, max_index, &low, &high);
- /* debug_printf("buffer range: %p %p range %d max index %u\n",
- low, high, high - low, max_index); */
-
- offset0 = low;
- if (userSpace) {
+ if (bufobj && bufobj->Name) {
+ vbuffer->buffer = NULL;
+ pipe_resource_reference(&vbuffer->buffer, stobj->buffer);
+ vbuffer->buffer_offset = pointer_to_offset(low_addr);
+ } else {
vbuffer->buffer =
- pipe_user_buffer_create(pipe->screen, (void *) low, high - low,
+ pipe_user_buffer_create(pipe->screen, (void*)low_addr,
+ stride * (max_index + 1),
PIPE_BIND_VERTEX_BUFFER);
vbuffer->buffer_offset = 0;
- }
- else {
- vbuffer->buffer = NULL;
- pipe_resource_reference(&vbuffer->buffer, stobj->buffer);
- vbuffer->buffer_offset = pointer_to_offset(low);
+
+ /* Track user vertex buffers. */
+ pipe_resource_reference(&st->user_vb[0], vbuffer->buffer);
+ st->user_vb_stride[0] = stride;
+ st->num_user_vbs = 1;
}
vbuffer->stride = stride; /* in bytes */
- vbuffer->max_index = max_index;
}
- /*
- if (arrays[mesaAttr]->InstanceDivisor)
- vbuffer[attr].max_index = arrays[mesaAttr]->_MaxElement;
- else
- vbuffer[attr].max_index = max_index;
- */
-
velements[attr].src_offset =
- (unsigned) (arrays[mesaAttr]->Ptr - offset0);
+ (unsigned) (arrays[mesaAttr]->Ptr - low_addr);
velements[attr].instance_divisor = arrays[mesaAttr]->InstanceDivisor;
velements[attr].vertex_buffer_index = 0;
velements[attr].src_format =
@@ -419,10 +364,9 @@ setup_non_interleaved_attribs(struct gl_context *ctx,
const struct st_vertex_program *vp,
const struct st_vp_variant *vpv,
const struct gl_client_array **arrays,
- GLuint max_index,
- GLboolean *userSpace,
struct pipe_vertex_buffer vbuffer[],
- struct pipe_vertex_element velements[])
+ struct pipe_vertex_element velements[],
+ unsigned max_index)
{
struct st_context *st = st_context(ctx);
struct pipe_context *pipe = st->pipe;
@@ -433,8 +377,6 @@ setup_non_interleaved_attribs(struct gl_context *ctx,
struct gl_buffer_object *bufobj = arrays[mesaAttr]->BufferObj;
GLsizei stride = arrays[mesaAttr]->StrideB;
- *userSpace = GL_FALSE;
-
if (bufobj && bufobj->Name) {
/* Attribute data is in a VBO.
* Recall that for VBOs, the gl_client_array->Ptr field is
@@ -442,37 +384,23 @@ setup_non_interleaved_attribs(struct gl_context *ctx,
*/
struct st_buffer_object *stobj = st_buffer_object(bufobj);
assert(stobj->buffer);
- /*printf("stobj %u = %p\n", attr, (void*) stobj);*/
vbuffer[attr].buffer = NULL;
pipe_resource_reference(&vbuffer[attr].buffer, stobj->buffer);
vbuffer[attr].buffer_offset = pointer_to_offset(arrays[mesaAttr]->Ptr);
}
else {
- /* attribute data is in user-space memory, not a VBO */
- uint bytes;
- /*printf("user-space array %d stride %d\n", attr, stride);*/
-
- *userSpace = GL_TRUE;
-
/* wrap user data */
if (arrays[mesaAttr]->Ptr) {
- /* user's vertex array */
- if (arrays[mesaAttr]->StrideB) {
- bytes = arrays[mesaAttr]->StrideB * (max_index + 1);
- }
- else {
- bytes = arrays[mesaAttr]->Size
- * _mesa_sizeof_type(arrays[mesaAttr]->Type);
- }
vbuffer[attr].buffer =
pipe_user_buffer_create(pipe->screen,
- (void *) arrays[mesaAttr]->Ptr, bytes,
+ (void *) arrays[mesaAttr]->Ptr,
+ stride * (max_index + 1),
PIPE_BIND_VERTEX_BUFFER);
}
else {
/* no array, use ctx->Current.Attrib[] value */
- bytes = sizeof(ctx->Current.Attrib[0]);
+ uint bytes = sizeof(ctx->Current.Attrib[0]);
vbuffer[attr].buffer =
pipe_user_buffer_create(pipe->screen,
(void *) ctx->Current.Attrib[mesaAttr],
@@ -482,16 +410,15 @@ setup_non_interleaved_attribs(struct gl_context *ctx,
}
vbuffer[attr].buffer_offset = 0;
- }
- assert(velements[attr].src_offset <= 2048); /* 11-bit field */
+ /* Track user vertex buffers. */
+ pipe_resource_reference(&st->user_vb[attr], vbuffer->buffer);
+ st->user_vb_stride[attr] = stride;
+ st->num_user_vbs = MAX2(st->num_user_vbs, attr+1);
+ }
/* common-case setup */
vbuffer[attr].stride = stride; /* in bytes */
- if (arrays[mesaAttr]->InstanceDivisor)
- vbuffer[attr].max_index = arrays[mesaAttr]->_MaxElement;
- else
- vbuffer[attr].max_index = max_index;
velements[attr].src_offset = 0;
velements[attr].instance_divisor = arrays[mesaAttr]->InstanceDivisor;
@@ -609,6 +536,62 @@ translate_prim(const struct gl_context *ctx, unsigned prim)
}
+static void
+st_validate_varrays(struct gl_context *ctx,
+ const struct gl_client_array **arrays,
+ unsigned max_index)
+{
+ struct st_context *st = st_context(ctx);
+ const struct st_vertex_program *vp;
+ const struct st_vp_variant *vpv;
+ struct pipe_vertex_buffer vbuffer[PIPE_MAX_SHADER_INPUTS];
+ struct pipe_vertex_element velements[PIPE_MAX_ATTRIBS];
+ unsigned num_vbuffers, num_velements;
+ GLuint attr;
+ unsigned i;
+
+ /* must get these after state validation! */
+ vp = st->vp;
+ vpv = st->vp_variant;
+
+ memset(velements, 0, sizeof(struct pipe_vertex_element) * vpv->num_inputs);
+
+ /* Unreference any user vertex buffers. */
+ for (i = 0; i < st->num_user_vbs; i++) {
+ pipe_resource_reference(&st->user_vb[i], NULL);
+ }
+ st->num_user_vbs = 0;
+
+ /*
+ * Setup the vbuffer[] and velements[] arrays.
+ */
+ if (is_interleaved_arrays(vp, vpv, arrays)) {
+ setup_interleaved_attribs(ctx, vp, vpv, arrays, vbuffer, velements,
+ max_index);
+ num_vbuffers = 1;
+ num_velements = vpv->num_inputs;
+ if (num_velements == 0)
+ num_vbuffers = 0;
+ }
+ else {
+ setup_non_interleaved_attribs(ctx, vp, vpv, arrays,
+ vbuffer, velements, max_index);
+ num_vbuffers = vpv->num_inputs;
+ num_velements = vpv->num_inputs;
+ }
+
+ cso_set_vertex_buffers(st->cso_context, num_vbuffers, vbuffer);
+ cso_set_vertex_elements(st->cso_context, num_velements, velements);
+
+ /* unreference buffers (frees wrapped user-space buffer objects)
+ * This is OK, because the pipe driver should reference buffers by itself
+ * in set_vertex_buffers. */
+ for (attr = 0; attr < num_vbuffers; attr++) {
+ pipe_resource_reference(&vbuffer[attr].buffer, NULL);
+ assert(!vbuffer[attr].buffer);
+ }
+}
+
/**
* This function gets plugged into the VBO module and is called when
@@ -627,90 +610,79 @@ st_draw_vbo(struct gl_context *ctx,
{
struct st_context *st = st_context(ctx);
struct pipe_context *pipe = st->pipe;
- const struct st_vertex_program *vp;
- const struct st_vp_variant *vpv;
- struct pipe_vertex_buffer vbuffer[PIPE_MAX_SHADER_INPUTS];
- GLuint attr;
- struct pipe_vertex_element velements[PIPE_MAX_ATTRIBS];
- unsigned num_vbuffers, num_velements;
struct pipe_index_buffer ibuffer;
- GLboolean userSpace = GL_FALSE;
- GLboolean vertDataEdgeFlags;
struct pipe_draw_info info;
unsigned i;
+ GLboolean new_array =
+ st->dirty.st && (st->dirty.mesa & (_NEW_ARRAY | _NEW_PROGRAM)) != 0;
/* Mesa core state should have been validated already */
assert(ctx->NewState == 0x0);
- /* Gallium probably doesn't want this in some cases. */
- if (!index_bounds_valid)
- if (!vbo_all_varyings_in_vbos(arrays))
- vbo_get_minmax_index(ctx, prims, ib, &min_index, &max_index);
+ if (ib) {
+ /* Gallium probably doesn't want this in some cases. */
+ if (!index_bounds_valid)
+ if (!vbo_all_varyings_in_vbos(arrays))
+ vbo_get_minmax_index(ctx, prims, ib, &min_index, &max_index);
+ } else {
+ /* Get min/max index for non-indexed drawing. */
+ min_index = ~0;
+ max_index = 0;
+
+ for (i = 0; i < nr_prims; i++) {
+ min_index = MIN2(min_index, prims[i].start);
+ max_index = MAX2(max_index, prims[i].start + prims[i].count - 1);
+ }
+ }
- /* sanity check for pointer arithmetic below */
- assert(sizeof(arrays[0]->Ptr[0]) == 1);
+ /* Validate state. */
+ if (st->dirty.st) {
+ GLboolean vertDataEdgeFlags;
- vertDataEdgeFlags = arrays[VERT_ATTRIB_EDGEFLAG]->BufferObj &&
- arrays[VERT_ATTRIB_EDGEFLAG]->BufferObj->Name;
- if (vertDataEdgeFlags != st->vertdata_edgeflags) {
- st->vertdata_edgeflags = vertDataEdgeFlags;
- st->dirty.st |= ST_NEW_EDGEFLAGS_DATA;
- }
+ /* sanity check for pointer arithmetic below */
+ assert(sizeof(arrays[0]->Ptr[0]) == 1);
- st_validate_state(st);
+ vertDataEdgeFlags = arrays[VERT_ATTRIB_EDGEFLAG]->BufferObj &&
+ arrays[VERT_ATTRIB_EDGEFLAG]->BufferObj->Name;
+ if (vertDataEdgeFlags != st->vertdata_edgeflags) {
+ st->vertdata_edgeflags = vertDataEdgeFlags;
+ st->dirty.st |= ST_NEW_EDGEFLAGS_DATA;
+ }
- /* must get these after state validation! */
- vp = st->vp;
- vpv = st->vp_variant;
+ st_validate_state(st);
+
+ if (new_array) {
+ st_validate_varrays(ctx, arrays, max_index);
+ }
#if 0
- if (MESA_VERBOSE & VERBOSE_GLSL) {
- check_uniforms(ctx);
- }
+ if (MESA_VERBOSE & VERBOSE_GLSL) {
+ check_uniforms(ctx);
+ }
#else
- (void) check_uniforms;
+ (void) check_uniforms;
#endif
-
- memset(velements, 0, sizeof(struct pipe_vertex_element) * vpv->num_inputs);
- /*
- * Setup the vbuffer[] and velements[] arrays.
- */
- if (is_interleaved_arrays(vp, vpv, arrays, &userSpace)) {
- /*printf("Draw interleaved\n");*/
- setup_interleaved_attribs(ctx, vp, vpv, arrays, max_index, userSpace,
- vbuffer, velements);
- num_vbuffers = 1;
- num_velements = vpv->num_inputs;
- if (num_velements == 0)
- num_vbuffers = 0;
- }
- else {
- /*printf("Draw non-interleaved\n");*/
- setup_non_interleaved_attribs(ctx, vp, vpv, arrays, max_index,
- &userSpace, vbuffer, velements);
- num_vbuffers = vpv->num_inputs;
- num_velements = vpv->num_inputs;
}
-#if 0
- {
- GLuint i;
- for (i = 0; i < num_vbuffers; i++) {
- printf("buffers[%d].stride = %u\n", i, vbuffer[i].stride);
- printf("buffers[%d].max_index = %u\n", i, vbuffer[i].max_index);
- printf("buffers[%d].buffer_offset = %u\n", i, vbuffer[i].buffer_offset);
- printf("buffers[%d].buffer = %p\n", i, (void*) vbuffer[i].buffer);
- }
- for (i = 0; i < num_velements; i++) {
- printf("vlements[%d].vbuffer_index = %u\n", i, velements[i].vertex_buffer_index);
- printf("vlements[%d].src_offset = %u\n", i, velements[i].src_offset);
- printf("vlements[%d].format = %s\n", i, util_format_name(velements[i].src_format));
+ /* Notify the driver that the content of user buffers may have been
+ * changed. */
+ if (!new_array && st->num_user_vbs) {
+ for (i = 0; i < st->num_user_vbs; i++) {
+ if (st->user_vb[i]) {
+ unsigned stride = st->user_vb_stride[i];
+
+ if (stride) {
+ pipe->redefine_user_buffer(pipe, st->user_vb[i],
+ min_index * stride,
+ (max_index + 1 - min_index) * stride);
+ } else {
+ /* stride == 0 */
+ pipe->redefine_user_buffer(pipe, st->user_vb[i],
+ 0, st->user_vb[i]->width0);
+ }
+ }
}
}
-#endif
-
- pipe->set_vertex_buffers(pipe, num_vbuffers, vbuffer);
- cso_set_vertex_elements(st->cso_context, num_velements, velements);
setup_index_buffer(ctx, ib, &ibuffer);
pipe->set_index_buffer(pipe, &ibuffer);
@@ -744,17 +716,6 @@ st_draw_vbo(struct gl_context *ctx,
}
pipe_resource_reference(&ibuffer.buffer, NULL);
-
- /* unreference buffers (frees wrapped user-space buffer objects) */
- for (attr = 0; attr < num_vbuffers; attr++) {
- pipe_resource_reference(&vbuffer[attr].buffer, NULL);
- assert(!vbuffer[attr].buffer);
- }
-
- if (userSpace)
- {
- pipe->set_vertex_buffers(pipe, 0, NULL);
- }
}
diff --git a/mesalib/src/mesa/state_tracker/st_draw_feedback.c b/mesalib/src/mesa/state_tracker/st_draw_feedback.c
index 2ecb8e202..f0734eae0 100644
--- a/mesalib/src/mesa/state_tracker/st_draw_feedback.c
+++ b/mesalib/src/mesa/state_tracker/st_draw_feedback.c
@@ -179,7 +179,6 @@ st_feedback_draw_vbo(struct gl_context *ctx,
/* common-case setup */
vbuffers[attr].stride = arrays[mesaAttr]->StrideB; /* in bytes */
- vbuffers[attr].max_index = max_index;
velements[attr].instance_divisor = 0;
velements[attr].vertex_buffer_index = attr;
velements[attr].src_format =
diff --git a/mesalib/src/mesa/state_tracker/st_gen_mipmap.c b/mesalib/src/mesa/state_tracker/st_gen_mipmap.c
index 0be66a2c2..5fcd806e0 100644
--- a/mesalib/src/mesa/state_tracker/st_gen_mipmap.c
+++ b/mesalib/src/mesa/state_tracker/st_gen_mipmap.c
@@ -105,13 +105,12 @@ st_render_mipmap(struct st_context *st,
static void
decompress_image(enum pipe_format format,
const uint8_t *src, uint8_t *dst,
- unsigned width, unsigned height)
+ unsigned width, unsigned height, unsigned src_stride)
{
const struct util_format_description *desc = util_format_description(format);
const uint bw = util_format_get_blockwidth(format);
const uint bh = util_format_get_blockheight(format);
const uint dst_stride = 4 * MAX2(width, bw);
- const uint src_stride = util_format_get_stride(format, width);
desc->unpack_rgba_8unorm(dst, dst_stride, src, src_stride, width, height);
@@ -144,10 +143,9 @@ decompress_image(enum pipe_format format,
static void
compress_image(enum pipe_format format,
const uint8_t *src, uint8_t *dst,
- unsigned width, unsigned height)
+ unsigned width, unsigned height, unsigned dst_stride)
{
const struct util_format_description *desc = util_format_description(format);
- const uint dst_stride = util_format_get_stride(format, width);
const uint src_stride = 4 * width;
desc->pack_rgba_8unorm(dst, dst_stride, src, src_stride, width, height);
@@ -236,7 +234,7 @@ fallback_generate_mipmap(struct gl_context *ctx, GLenum target,
dstTemp = malloc(dstWidth2 * dstHeight2 * comps + 000);
/* decompress the src image: srcData -> srcTemp */
- decompress_image(format, srcData, srcTemp, srcWidth, srcHeight);
+ decompress_image(format, srcData, srcTemp, srcWidth, srcHeight, srcTrans->stride);
_mesa_generate_mipmap_level(target, datatype, comps,
0 /*border*/,
@@ -248,7 +246,7 @@ fallback_generate_mipmap(struct gl_context *ctx, GLenum target,
dstWidth2); /* stride in texels */
/* compress the new image: dstTemp -> dstData */
- compress_image(format, dstTemp, dstData, dstWidth, dstHeight);
+ compress_image(format, dstTemp, dstData, dstWidth, dstHeight, dstTrans->stride);
free(srcTemp);
free(dstTemp);
diff --git a/mesalib/src/mesa/tnl/t_draw.c b/mesalib/src/mesa/tnl/t_draw.c
index 741f0ed3f..a2ca6225b 100644
--- a/mesalib/src/mesa/tnl/t_draw.c
+++ b/mesalib/src/mesa/tnl/t_draw.c
@@ -125,6 +125,43 @@ convert_half_to_float(const struct gl_client_array *input,
}
}
+/**
+ * \brief Convert fixed-point to floating-point.
+ *
+ * In OpenGL, a fixed-point number is a "signed 2's complement 16.16 scaled
+ * integer" (Table 2.2 of the OpenGL ES 2.0 spec).
+ *
+ * If the buffer has the \c normalized flag set, the formula
+ * \code normalize(x) := (2*x + 1) / (2^16 - 1) \endcode
+ * is used to map the fixed-point numbers into the range [-1, 1].
+ */
+static void
+convert_fixed_to_float(const struct gl_client_array *input,
+ const GLubyte *ptr, GLfloat *fptr,
+ GLuint count)
+{
+ GLuint i, j;
+ const GLint size = input->Size;
+
+ if (input->Normalized) {
+ for (i = 0; i < count; ++i) {
+ const GLfixed *in = (GLfixed *) ptr;
+ for (j = 0; j < size; ++j) {
+ *fptr++ = (GLfloat) (2 * in[j] + 1) / (GLfloat) ((1 << 16) - 1);
+ }
+ ptr += input->StrideB;
+ }
+ } else {
+ for (i = 0; i < count; ++i) {
+ const GLfixed *in = (GLfixed *) ptr;
+ for (j = 0; j < size; ++j) {
+ *fptr++ = in[j] / (GLfloat) (1 << 16);
+ }
+ ptr += input->StrideB;
+ }
+ }
+}
+
/* Adjust pointer to point at first requested element, convert to
* floating point, populate VB->AttribPtr[].
*/
@@ -174,6 +211,9 @@ static void _tnl_import_array( struct gl_context *ctx,
case GL_HALF_FLOAT:
convert_half_to_float(input, ptr, fptr, count, sz);
break;
+ case GL_FIXED:
+ convert_fixed_to_float(input, ptr, fptr, count);
+ break;
default:
assert(0);
break;
diff --git a/mesalib/src/mesa/vbo/vbo_exec_array.c b/mesalib/src/mesa/vbo/vbo_exec_array.c
index 13b54d59c..4304985aa 100644
--- a/mesalib/src/mesa/vbo/vbo_exec_array.c
+++ b/mesalib/src/mesa/vbo/vbo_exec_array.c
@@ -502,8 +502,13 @@ recalculate_input_bindings(struct gl_context *ctx)
static void
bind_arrays(struct gl_context *ctx)
{
+ if (!ctx->Array.RebindArrays) {
+ return;
+ }
+
bind_array_obj(ctx);
recalculate_input_bindings(ctx);
+ ctx->Array.RebindArrays = GL_FALSE;
}
diff --git a/mesalib/src/mesa/vbo/vbo_exec_draw.c b/mesalib/src/mesa/vbo/vbo_exec_draw.c
index 048f3d170..a2a098857 100644
--- a/mesalib/src/mesa/vbo/vbo_exec_draw.c
+++ b/mesalib/src/mesa/vbo/vbo_exec_draw.c
@@ -245,6 +245,7 @@ vbo_exec_bind_arrays( struct gl_context *ctx )
arrays[attr]._MaxElement = count; /* ??? */
varying_inputs |= 1 << attr;
+ ctx->NewState |= _NEW_ARRAY;
}
}
diff --git a/mesalib/src/mesa/vbo/vbo_save_draw.c b/mesalib/src/mesa/vbo/vbo_save_draw.c
index 6d8dbdb86..5a19b0d62 100644
--- a/mesalib/src/mesa/vbo/vbo_save_draw.c
+++ b/mesalib/src/mesa/vbo/vbo_save_draw.c
@@ -202,6 +202,7 @@ static void vbo_bind_vertex_list(struct gl_context *ctx,
buffer_offset += node->attrsz[src] * sizeof(GLfloat);
varying_inputs |= 1<<attr;
+ ctx->NewState |= _NEW_ARRAY;
}
}