X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fmesa%2Fprogram%2Fir_to_mesa.cpp;h=5c92bc9865de1e892c2fef1b26f28cb354c199b1;hb=9ff90b7230cceb362c7e0fe1c3d5029b8cbfe6bd;hp=ba0934c446dd5cd5e3850e9a967fe311321cafaa;hpb=47f305a4fcd23f859d097c6cc25a739547462939;p=mesa.git diff --git a/src/mesa/program/ir_to_mesa.cpp b/src/mesa/program/ir_to_mesa.cpp index ba0934c446d..5c92bc9865d 100644 --- a/src/mesa/program/ir_to_mesa.cpp +++ b/src/mesa/program/ir_to_mesa.cpp @@ -31,6 +31,7 @@ */ #include +#include "main/compiler.h" #include "ir.h" #include "ir_visitor.h" #include "ir_print_visitor.h" @@ -43,14 +44,17 @@ extern "C" { #include "main/mtypes.h" +#include "main/shaderapi.h" #include "main/shaderobj.h" #include "main/uniforms.h" +#include "program/hash_table.h" #include "program/prog_instruction.h" #include "program/prog_optimize.h" #include "program/prog_print.h" #include "program/program.h" #include "program/prog_uniform.h" #include "program/prog_parameter.h" +#include "program/sampler.h" } static int swizzle_for_size(int size); @@ -75,6 +79,10 @@ typedef struct ir_to_mesa_src_reg { ir_to_mesa_src_reg() { this->file = PROGRAM_UNDEFINED; + this->index = 0; + this->swizzle = 0; + this->negate = 0; + this->reladdr = NULL; } int file; /**< PROGRAM_* from Mesa */ @@ -98,6 +106,18 @@ extern ir_to_mesa_src_reg ir_to_mesa_undef; class ir_to_mesa_instruction : public exec_node { public: + /* Callers of this talloc-based new need not call delete. It's + * easier to just talloc_free 'ctx' (or any of its ancestors). */ + static void* operator new(size_t size, void *ctx) + { + void *node; + + node = talloc_zero_size(ctx, size); + assert(node != NULL); + + return node; + } + enum prog_opcode op; ir_to_mesa_dst_reg dst_reg; ir_to_mesa_src_reg src_reg[3]; @@ -160,11 +180,14 @@ public: class ir_to_mesa_visitor : public ir_visitor { public: ir_to_mesa_visitor(); + ~ir_to_mesa_visitor(); function_entry *current_function; GLcontext *ctx; struct gl_program *prog; + struct gl_shader_program *shader_program; + struct gl_shader_compiler_options *options; int next_temp; @@ -252,21 +275,6 @@ public: GLboolean try_emit_mad(ir_expression *ir, int mul_operand); - int add_uniform(const char *name, - const glsl_type *type, - ir_constant *constant); - void add_aggregate_uniform(ir_instruction *ir, - const char *name, - const struct glsl_type *type, - ir_constant *constant, - struct ir_to_mesa_dst_reg temp); - - int *sampler_map; - int sampler_map_size; - - void map_sampler(int location, int sampler); - int get_sampler_number(int location); - void *mem_ctx; }; @@ -280,6 +288,18 @@ ir_to_mesa_dst_reg ir_to_mesa_address_reg = { PROGRAM_ADDRESS, 0, WRITEMASK_X, COND_TR, NULL }; +static void fail_link(struct gl_shader_program *prog, const char *fmt, ...) PRINTFLIKE(2, 3); + +static void fail_link(struct gl_shader_program *prog, const char *fmt, ...) + { + va_list args; + va_start(args, fmt); + prog->InfoLog = talloc_vasprintf_append(prog->InfoLog, fmt, args); + va_end(args); + + prog->LinkStatus = GL_FALSE; + } + static int swizzle_for_size(int size) { int size_swizzles[4] = { @@ -355,6 +375,7 @@ ir_to_mesa_visitor::ir_to_mesa_emit_op1(ir_instruction *ir, ir_to_mesa_dst_reg dst, ir_to_mesa_src_reg src0) { + assert(dst.writemask != 0); return ir_to_mesa_emit_op3(ir, op, dst, src0, ir_to_mesa_undef, ir_to_mesa_undef); } @@ -369,25 +390,6 @@ ir_to_mesa_visitor::ir_to_mesa_emit_op0(ir_instruction *ir, ir_to_mesa_undef); } -void -ir_to_mesa_visitor::map_sampler(int location, int sampler) -{ - if (this->sampler_map_size <= location) { - this->sampler_map = talloc_realloc(this->mem_ctx, this->sampler_map, - int, location + 1); - this->sampler_map_size = location + 1; - } - - this->sampler_map[location] = sampler; -} - -int -ir_to_mesa_visitor::get_sampler_number(int location) -{ - assert(location < this->sampler_map_size); - return this->sampler_map[location]; -} - inline ir_to_mesa_dst_reg ir_to_mesa_dst_reg_from_src(ir_to_mesa_src_reg reg) { @@ -515,8 +517,14 @@ type_size(const struct glsl_type *type) size += type_size(type->fields.structure[i].type); } return size; + case GLSL_TYPE_SAMPLER: + /* Samplers take up one slot in UNIFORMS[], but they're baked in + * at link time. + */ + return 1; default: assert(0); + return 0; } } @@ -578,18 +586,152 @@ ir_to_mesa_visitor::visit(ir_variable *ir) fp->OriginUpperLeft = ir->origin_upper_left; fp->PixelCenterInteger = ir->pixel_center_integer; } + + if (ir->mode == ir_var_uniform && strncmp(ir->name, "gl_", 3) == 0) { + unsigned int i; + const struct gl_builtin_uniform_desc *statevar; + + for (i = 0; _mesa_builtin_uniform_desc[i].name; i++) { + if (strcmp(ir->name, _mesa_builtin_uniform_desc[i].name) == 0) + break; + } + + if (!_mesa_builtin_uniform_desc[i].name) { + fail_link(this->shader_program, + "Failed to find builtin uniform `%s'\n", ir->name); + return; + } + + statevar = &_mesa_builtin_uniform_desc[i]; + + int array_count; + if (ir->type->is_array()) { + array_count = ir->type->length; + } else { + array_count = 1; + } + + /* Check if this statevar's setup in the STATE file exactly + * matches how we'll want to reference it as a + * struct/array/whatever. If not, then we need to move it into + * temporary storage and hope that it'll get copy-propagated + * out. + */ + for (i = 0; i < statevar->num_elements; i++) { + if (statevar->elements[i].swizzle != SWIZZLE_XYZW) { + break; + } + } + + struct variable_storage *storage; + ir_to_mesa_dst_reg dst; + if (i == statevar->num_elements) { + /* We'll set the index later. */ + storage = new(mem_ctx) variable_storage(ir, PROGRAM_STATE_VAR, -1); + this->variables.push_tail(storage); + + dst = ir_to_mesa_undef_dst; + } else { + storage = new(mem_ctx) variable_storage(ir, PROGRAM_TEMPORARY, + this->next_temp); + this->variables.push_tail(storage); + this->next_temp += type_size(ir->type); + + dst = ir_to_mesa_dst_reg_from_src(ir_to_mesa_src_reg(PROGRAM_TEMPORARY, + storage->index, + NULL)); + } + + + for (int a = 0; a < array_count; a++) { + for (unsigned int i = 0; i < statevar->num_elements; i++) { + struct gl_builtin_uniform_element *element = &statevar->elements[i]; + int tokens[STATE_LENGTH]; + + memcpy(tokens, element->tokens, sizeof(element->tokens)); + if (ir->type->is_array()) { + tokens[1] = a; + } + + int index = _mesa_add_state_reference(this->prog->Parameters, + (gl_state_index *)tokens); + + if (storage->file == PROGRAM_STATE_VAR) { + if (storage->index == -1) { + storage->index = index; + } else { + assert(index == + (int)(storage->index + a * statevar->num_elements + i)); + } + } else { + ir_to_mesa_src_reg src(PROGRAM_STATE_VAR, index, NULL); + src.swizzle = element->swizzle; + ir_to_mesa_emit_op1(ir, OPCODE_MOV, dst, src); + /* even a float takes up a whole vec4 reg in a struct/array. */ + dst.index++; + } + } + } + if (storage->file == PROGRAM_TEMPORARY && + dst.index != storage->index + type_size(ir->type)) { + fail_link(this->shader_program, + "failed to load builtin uniform `%s' (%d/%d regs loaded)\n", + ir->name, dst.index - storage->index, + type_size(ir->type)); + } + } } void ir_to_mesa_visitor::visit(ir_loop *ir) { - assert(!ir->from); - assert(!ir->to); - assert(!ir->increment); - assert(!ir->counter); + ir_dereference_variable *counter = NULL; + + if (ir->counter != NULL) + counter = new(ir) ir_dereference_variable(ir->counter); + + if (ir->from != NULL) { + assert(ir->counter != NULL); + + ir_assignment *a = new(ir) ir_assignment(counter, ir->from, NULL); + + a->accept(this); + delete a; + } ir_to_mesa_emit_op0(NULL, OPCODE_BGNLOOP); + + if (ir->to) { + ir_expression *e = + new(ir) ir_expression(ir->cmp, glsl_type::bool_type, + counter, ir->to); + ir_if *if_stmt = new(ir) ir_if(e); + + ir_loop_jump *brk = new(ir) ir_loop_jump(ir_loop_jump::jump_break); + + if_stmt->then_instructions.push_tail(brk); + + if_stmt->accept(this); + + delete if_stmt; + delete e; + delete brk; + } + visit_exec_list(&ir->body_instructions, this); + + if (ir->increment) { + ir_expression *e = + new(ir) ir_expression(ir_binop_add, counter->type, + counter, ir->increment); + + ir_assignment *a = new(ir) ir_assignment(counter, e, NULL); + + a->accept(this); + delete a; + delete e; + } + ir_to_mesa_emit_op0(NULL, OPCODE_ENDLOOP); } @@ -717,6 +859,12 @@ ir_to_mesa_visitor::visit(ir_expression *ir) assert(!ir->operands[operand]->type->is_matrix()); } + int vector_elements = ir->operands[0]->type->vector_elements; + if (ir->operands[1]) { + vector_elements = MAX2(vector_elements, + ir->operands[1]->type->vector_elements); + } + this->result.file = PROGRAM_UNDEFINED; /* Storage for our result. Ideally for an assignment we'd be using @@ -750,15 +898,12 @@ ir_to_mesa_visitor::visit(ir_expression *ir) ir_to_mesa_emit_scalar_op1(ir, OPCODE_RCP, result_dst, op[0]); break; - case ir_unop_exp: - ir_to_mesa_emit_scalar_op2(ir, OPCODE_POW, result_dst, - src_reg_for_float(M_E), op[0]); - break; case ir_unop_exp2: ir_to_mesa_emit_scalar_op1(ir, OPCODE_EX2, result_dst, op[0]); break; + case ir_unop_exp: case ir_unop_log: - ir_to_mesa_emit_scalar_op1(ir, OPCODE_LOG, result_dst, op[0]); + assert(!"not reached: should be handled by ir_explog_to_explog2"); break; case ir_unop_log2: ir_to_mesa_emit_scalar_op1(ir, OPCODE_LG2, result_dst, op[0]); @@ -777,6 +922,16 @@ ir_to_mesa_visitor::visit(ir_expression *ir) ir_to_mesa_emit_op1(ir, OPCODE_DDY, result_dst, op[0]); break; + case ir_unop_noise: { + const enum prog_opcode opcode = + prog_opcode(OPCODE_NOISE1 + + (ir->operands[0]->type->vector_elements) - 1); + assert((opcode >= OPCODE_NOISE1) && (opcode <= OPCODE_NOISE4)); + + ir_to_mesa_emit_op1(ir, opcode, result_dst, op[0]); + break; + } + case ir_binop_add: ir_to_mesa_emit_op2(ir, OPCODE_ADD, result_dst, op[0], op[1]); break; @@ -808,10 +963,70 @@ ir_to_mesa_visitor::visit(ir_expression *ir) case ir_binop_equal: ir_to_mesa_emit_op2(ir, OPCODE_SEQ, result_dst, op[0], op[1]); break; - case ir_binop_logic_xor: case ir_binop_nequal: ir_to_mesa_emit_op2(ir, OPCODE_SNE, result_dst, op[0], op[1]); break; + case ir_binop_all_equal: + /* "==" operator producing a scalar boolean. */ + if (ir->operands[0]->type->is_vector() || + ir->operands[1]->type->is_vector()) { + ir_to_mesa_src_reg temp = get_temp(glsl_type::vec4_type); + ir_to_mesa_emit_op2(ir, OPCODE_SNE, + ir_to_mesa_dst_reg_from_src(temp), op[0], op[1]); + if (vector_elements == 4) + ir_to_mesa_emit_op2(ir, OPCODE_DP4, result_dst, temp, temp); + else if (vector_elements == 3) + ir_to_mesa_emit_op2(ir, OPCODE_DP3, result_dst, temp, temp); + else + ir_to_mesa_emit_op2(ir, OPCODE_DP2, result_dst, temp, temp); + ir_to_mesa_emit_op2(ir, OPCODE_SEQ, + result_dst, result_src, src_reg_for_float(0.0)); + } else { + ir_to_mesa_emit_op2(ir, OPCODE_SEQ, result_dst, op[0], op[1]); + } + break; + case ir_binop_any_nequal: + /* "!=" operator producing a scalar boolean. */ + if (ir->operands[0]->type->is_vector() || + ir->operands[1]->type->is_vector()) { + ir_to_mesa_src_reg temp = get_temp(glsl_type::vec4_type); + ir_to_mesa_emit_op2(ir, OPCODE_SNE, + ir_to_mesa_dst_reg_from_src(temp), op[0], op[1]); + if (vector_elements == 4) + ir_to_mesa_emit_op2(ir, OPCODE_DP4, result_dst, temp, temp); + else if (vector_elements == 3) + ir_to_mesa_emit_op2(ir, OPCODE_DP3, result_dst, temp, temp); + else + ir_to_mesa_emit_op2(ir, OPCODE_DP2, result_dst, temp, temp); + ir_to_mesa_emit_op2(ir, OPCODE_SNE, + result_dst, result_src, src_reg_for_float(0.0)); + } else { + ir_to_mesa_emit_op2(ir, OPCODE_SNE, result_dst, op[0], op[1]); + } + break; + + case ir_unop_any: + switch (ir->operands[0]->type->vector_elements) { + case 4: + ir_to_mesa_emit_op2(ir, OPCODE_DP4, result_dst, op[0], op[0]); + break; + case 3: + ir_to_mesa_emit_op2(ir, OPCODE_DP3, result_dst, op[0], op[0]); + break; + case 2: + ir_to_mesa_emit_op2(ir, OPCODE_DP2, result_dst, op[0], op[0]); + break; + default: + assert(!"unreached: ir_unop_any of non-bvec"); + break; + } + ir_to_mesa_emit_op2(ir, OPCODE_SNE, + result_dst, result_src, src_reg_for_float(0.0)); + break; + + case ir_binop_logic_xor: + ir_to_mesa_emit_op2(ir, OPCODE_SNE, result_dst, op[0], op[1]); + break; case ir_binop_logic_or: /* This could be a saturated add and skip the SNE. */ @@ -855,11 +1070,13 @@ ir_to_mesa_visitor::visit(ir_expression *ir) break; case ir_unop_sqrt: + /* sqrt(x) = x * rsq(x). */ ir_to_mesa_emit_scalar_op1(ir, OPCODE_RSQ, result_dst, op[0]); - ir_to_mesa_emit_scalar_op1(ir, OPCODE_RCP, result_dst, result_src); - /* For incoming channels < 0, set the result to 0. */ + ir_to_mesa_emit_op2(ir, OPCODE_MUL, result_dst, result_src, op[0]); + /* For incoming channels <= 0, set the result to 0. */ + op[0].negate = ~op[0].negate; ir_to_mesa_emit_op3(ir, OPCODE_CMP, result_dst, - op[0], src_reg_for_float(0.0), result_src); + op[0], result_src, src_reg_for_float(0.0)); break; case ir_unop_rsq: ir_to_mesa_emit_scalar_op1(ir, OPCODE_RSQ, result_dst, op[0]); @@ -876,7 +1093,7 @@ ir_to_mesa_visitor::visit(ir_expression *ir) case ir_unop_f2b: case ir_unop_i2b: ir_to_mesa_emit_op2(ir, OPCODE_SNE, result_dst, - result_src, src_reg_for_float(0.0)); + op[0], src_reg_for_float(0.0)); break; case ir_unop_trunc: ir_to_mesa_emit_op1(ir, OPCODE_TRUNC, result_dst, op[0]); @@ -966,480 +1183,16 @@ ir_to_mesa_visitor::visit(ir_swizzle *ir) this->result = src_reg; } -static const struct { - const char *name; - const char *field; - int tokens[STATE_LENGTH]; - int swizzle; - bool array_indexed; -} statevars[] = { - {"gl_DepthRange", "near", - {STATE_DEPTH_RANGE, 0, 0}, SWIZZLE_XXXX}, - {"gl_DepthRange", "far", - {STATE_DEPTH_RANGE, 0, 0}, SWIZZLE_YYYY}, - {"gl_DepthRange", "diff", - {STATE_DEPTH_RANGE, 0, 0}, SWIZZLE_ZZZZ}, - - {"gl_ClipPlane", NULL, - {STATE_CLIPPLANE, 0, 0}, SWIZZLE_XYZW, true} -, - {"gl_Point", "size", - {STATE_POINT_SIZE}, SWIZZLE_XXXX}, - {"gl_Point", "sizeMin", - {STATE_POINT_SIZE}, SWIZZLE_YYYY}, - {"gl_Point", "sizeMax", - {STATE_POINT_SIZE}, SWIZZLE_ZZZZ}, - {"gl_Point", "fadeThresholdSize", - {STATE_POINT_SIZE}, SWIZZLE_WWWW}, - {"gl_Point", "distanceConstantAttenuation", - {STATE_POINT_ATTENUATION}, SWIZZLE_XXXX}, - {"gl_Point", "distanceLinearAttenuation", - {STATE_POINT_ATTENUATION}, SWIZZLE_YYYY}, - {"gl_Point", "distanceQuadraticAttenuation", - {STATE_POINT_ATTENUATION}, SWIZZLE_ZZZZ}, - - {"gl_FrontMaterial", "emission", - {STATE_MATERIAL, 0, STATE_EMISSION}, SWIZZLE_XYZW}, - {"gl_FrontMaterial", "ambient", - {STATE_MATERIAL, 0, STATE_AMBIENT}, SWIZZLE_XYZW}, - {"gl_FrontMaterial", "diffuse", - {STATE_MATERIAL, 0, STATE_DIFFUSE}, SWIZZLE_XYZW}, - {"gl_FrontMaterial", "specular", - {STATE_MATERIAL, 0, STATE_SPECULAR}, SWIZZLE_XYZW}, - {"gl_FrontMaterial", "shininess", - {STATE_MATERIAL, 0, STATE_SHININESS}, SWIZZLE_XXXX}, - - {"gl_BackMaterial", "emission", - {STATE_MATERIAL, 1, STATE_EMISSION}, SWIZZLE_XYZW}, - {"gl_BackMaterial", "ambient", - {STATE_MATERIAL, 1, STATE_AMBIENT}, SWIZZLE_XYZW}, - {"gl_BackMaterial", "diffuse", - {STATE_MATERIAL, 1, STATE_DIFFUSE}, SWIZZLE_XYZW}, - {"gl_BackMaterial", "specular", - {STATE_MATERIAL, 1, STATE_SPECULAR}, SWIZZLE_XYZW}, - {"gl_BackMaterial", "shininess", - {STATE_MATERIAL, 1, STATE_SHININESS}, SWIZZLE_XXXX}, - - {"gl_LightSource", "ambient", - {STATE_LIGHT, 0, STATE_AMBIENT}, SWIZZLE_XYZW, true}, - {"gl_LightSource", "diffuse", - {STATE_LIGHT, 0, STATE_DIFFUSE}, SWIZZLE_XYZW, true}, - {"gl_LightSource", "specular", - {STATE_LIGHT, 0, STATE_SPECULAR}, SWIZZLE_XYZW, true}, - {"gl_LightSource", "position", - {STATE_LIGHT, 0, STATE_POSITION}, SWIZZLE_XYZW, true}, - {"gl_LightSource", "halfVector", - {STATE_LIGHT, 0, STATE_HALF_VECTOR}, SWIZZLE_XYZW, true}, - {"gl_LightSource", "spotDirection", - {STATE_LIGHT, 0, STATE_SPOT_DIRECTION}, SWIZZLE_XYZW, true}, - {"gl_LightSource", "spotCosCutoff", - {STATE_LIGHT, 0, STATE_SPOT_DIRECTION}, SWIZZLE_WWWW, true}, - {"gl_LightSource", "spotCutoff", - {STATE_LIGHT, 0, STATE_SPOT_CUTOFF}, SWIZZLE_XXXX, true}, - {"gl_LightSource", "spotExponent", - {STATE_LIGHT, 0, STATE_ATTENUATION}, SWIZZLE_WWWW, true}, - {"gl_LightSource", "constantAttenuation", - {STATE_LIGHT, 0, STATE_ATTENUATION}, SWIZZLE_XXXX, true}, - {"gl_LightSource", "linearAttenuation", - {STATE_LIGHT, 0, STATE_ATTENUATION}, SWIZZLE_YYYY, true}, - {"gl_LightSource", "quadraticAttenuation", - {STATE_LIGHT, 0, STATE_ATTENUATION}, SWIZZLE_ZZZZ, true}, - - {"gl_LightModel", NULL, - {STATE_LIGHTMODEL_AMBIENT, 0}, SWIZZLE_XYZW}, - - {"gl_FrontLightModelProduct", NULL, - {STATE_LIGHTMODEL_SCENECOLOR, 0}, SWIZZLE_XYZW}, - {"gl_BackLightModelProduct", NULL, - {STATE_LIGHTMODEL_SCENECOLOR, 1}, SWIZZLE_XYZW}, - - {"gl_FrontLightProduct", "ambient", - {STATE_LIGHTPROD, 0, 0, STATE_AMBIENT}, SWIZZLE_XYZW, true}, - {"gl_FrontLightProduct", "diffuse", - {STATE_LIGHTPROD, 0, 0, STATE_DIFFUSE}, SWIZZLE_XYZW, true}, - {"gl_FrontLightProduct", "specular", - {STATE_LIGHTPROD, 0, 0, STATE_SPECULAR}, SWIZZLE_XYZW, true}, - - {"gl_BackLightProduct", "ambient", - {STATE_LIGHTPROD, 0, 1, STATE_AMBIENT}, SWIZZLE_XYZW, true}, - {"gl_BackLightProduct", "diffuse", - {STATE_LIGHTPROD, 0, 1, STATE_DIFFUSE}, SWIZZLE_XYZW, true}, - {"gl_BackLightProduct", "specular", - {STATE_LIGHTPROD, 0, 1, STATE_SPECULAR}, SWIZZLE_XYZW, true}, - - {"gl_TextureEnvColor", "ambient", - {STATE_TEXENV_COLOR, 0}, SWIZZLE_XYZW, true}, - - {"gl_EyePlaneS", NULL, - {STATE_TEXGEN, 0, STATE_TEXGEN_EYE_S}, SWIZZLE_XYZW, true}, - {"gl_EyePlaneT", NULL, - {STATE_TEXGEN, 0, STATE_TEXGEN_EYE_T}, SWIZZLE_XYZW, true}, - {"gl_EyePlaneR", NULL, - {STATE_TEXGEN, 0, STATE_TEXGEN_EYE_R}, SWIZZLE_XYZW, true}, - {"gl_EyePlaneQ", NULL, - {STATE_TEXGEN, 0, STATE_TEXGEN_EYE_Q}, SWIZZLE_XYZW, true}, - - {"gl_ObjectPlaneS", NULL, - {STATE_TEXGEN, 0, STATE_TEXGEN_OBJECT_S}, SWIZZLE_XYZW, true}, - {"gl_ObjectPlaneT", NULL, - {STATE_TEXGEN, 0, STATE_TEXGEN_OBJECT_T}, SWIZZLE_XYZW, true}, - {"gl_ObjectPlaneR", NULL, - {STATE_TEXGEN, 0, STATE_TEXGEN_OBJECT_R}, SWIZZLE_XYZW, true}, - {"gl_ObjectPlaneQ", NULL, - {STATE_TEXGEN, 0, STATE_TEXGEN_OBJECT_Q}, SWIZZLE_XYZW, true}, - - {"gl_Fog", "color", - {STATE_FOG_COLOR}, SWIZZLE_XYZW}, - {"gl_Fog", "density", - {STATE_FOG_PARAMS}, SWIZZLE_XXXX}, - {"gl_Fog", "start", - {STATE_FOG_PARAMS}, SWIZZLE_YYYY}, - {"gl_Fog", "end", - {STATE_FOG_PARAMS}, SWIZZLE_ZZZZ}, - {"gl_Fog", "scale", - {STATE_FOG_PARAMS}, SWIZZLE_WWWW}, -}; - -static ir_to_mesa_src_reg -get_builtin_uniform_reg(struct gl_program *prog, - const char *name, int array_index, const char *field) -{ - unsigned int i; - ir_to_mesa_src_reg src_reg; - int tokens[STATE_LENGTH]; - - for (i = 0; i < Elements(statevars); i++) { - if (strcmp(statevars[i].name, name) != 0) - continue; - if (!field && statevars[i].field) { - assert(!"FINISHME: whole-structure state var dereference"); - } - if (field && strcmp(statevars[i].field, field) != 0) - continue; - break; - } - - if (i == Elements(statevars)) { - printf("builtin uniform %s%s%s not found\n", - name, - field ? "." : "", - field ? field : ""); - abort(); - } - - memcpy(&tokens, statevars[i].tokens, sizeof(tokens)); - if (statevars[i].array_indexed) - tokens[1] = array_index; - - src_reg.file = PROGRAM_STATE_VAR; - src_reg.index = _mesa_add_state_reference(prog->Parameters, - (gl_state_index *)tokens); - src_reg.swizzle = statevars[i].swizzle; - src_reg.negate = 0; - src_reg.reladdr = false; - - return src_reg; -} - -static int -add_matrix_ref(struct gl_program *prog, int *tokens) -{ - int base_pos = -1; - int i; - - /* Add a ref for each column. It looks like the reason we do - * it this way is that _mesa_add_state_reference doesn't work - * for things that aren't vec4s, so the tokens[2]/tokens[3] - * range has to be equal. - */ - for (i = 0; i < 4; i++) { - tokens[2] = i; - tokens[3] = i; - int pos = _mesa_add_state_reference(prog->Parameters, - (gl_state_index *)tokens); - if (base_pos == -1) - base_pos = pos; - else - assert(base_pos + i == pos); - } - - return base_pos; -} - -static variable_storage * -get_builtin_matrix_ref(void *mem_ctx, struct gl_program *prog, ir_variable *var, - ir_rvalue *array_index) -{ - /* - * NOTE: The ARB_vertex_program extension specified that matrices get - * loaded in registers in row-major order. With GLSL, we want column- - * major order. So, we need to transpose all matrices here... - */ - static const struct { - const char *name; - int matrix; - int modifier; - } matrices[] = { - { "gl_ModelViewMatrix", STATE_MODELVIEW_MATRIX, STATE_MATRIX_TRANSPOSE }, - { "gl_ModelViewMatrixInverse", STATE_MODELVIEW_MATRIX, STATE_MATRIX_INVTRANS }, - { "gl_ModelViewMatrixTranspose", STATE_MODELVIEW_MATRIX, 0 }, - { "gl_ModelViewMatrixInverseTranspose", STATE_MODELVIEW_MATRIX, STATE_MATRIX_INVERSE }, - - { "gl_ProjectionMatrix", STATE_PROJECTION_MATRIX, STATE_MATRIX_TRANSPOSE }, - { "gl_ProjectionMatrixInverse", STATE_PROJECTION_MATRIX, STATE_MATRIX_INVTRANS }, - { "gl_ProjectionMatrixTranspose", STATE_PROJECTION_MATRIX, 0 }, - { "gl_ProjectionMatrixInverseTranspose", STATE_PROJECTION_MATRIX, STATE_MATRIX_INVERSE }, - - { "gl_ModelViewProjectionMatrix", STATE_MVP_MATRIX, STATE_MATRIX_TRANSPOSE }, - { "gl_ModelViewProjectionMatrixInverse", STATE_MVP_MATRIX, STATE_MATRIX_INVTRANS }, - { "gl_ModelViewProjectionMatrixTranspose", STATE_MVP_MATRIX, 0 }, - { "gl_ModelViewProjectionMatrixInverseTranspose", STATE_MVP_MATRIX, STATE_MATRIX_INVERSE }, - - { "gl_TextureMatrix", STATE_TEXTURE_MATRIX, STATE_MATRIX_TRANSPOSE }, - { "gl_TextureMatrixInverse", STATE_TEXTURE_MATRIX, STATE_MATRIX_INVTRANS }, - { "gl_TextureMatrixTranspose", STATE_TEXTURE_MATRIX, 0 }, - { "gl_TextureMatrixInverseTranspose", STATE_TEXTURE_MATRIX, STATE_MATRIX_INVERSE }, - - { "gl_NormalMatrix", STATE_MODELVIEW_MATRIX, STATE_MATRIX_INVERSE }, - - }; - unsigned int i; - variable_storage *entry; - - /* C++ gets angry when we try to use an int as a gl_state_index, so we use - * ints for gl_state_index. Make sure they're compatible. - */ - assert(sizeof(gl_state_index) == sizeof(int)); - - for (i = 0; i < Elements(matrices); i++) { - if (strcmp(var->name, matrices[i].name) == 0) { - int tokens[STATE_LENGTH]; - int base_pos = -1; - - tokens[0] = matrices[i].matrix; - tokens[4] = matrices[i].modifier; - if (matrices[i].matrix == STATE_TEXTURE_MATRIX) { - ir_constant *index = array_index->constant_expression_value(); - if (index) { - tokens[1] = index->value.i[0]; - base_pos = add_matrix_ref(prog, tokens); - } else { - for (i = 0; i < var->type->length; i++) { - tokens[1] = i; - int pos = add_matrix_ref(prog, tokens); - if (base_pos == -1) - base_pos = pos; - else - assert(base_pos + (int)i * 4 == pos); - } - } - } else { - tokens[1] = 0; /* unused array index */ - base_pos = add_matrix_ref(prog, tokens); - } - tokens[4] = matrices[i].modifier; - - entry = new(mem_ctx) variable_storage(var, - PROGRAM_STATE_VAR, - base_pos); - - return entry; - } - } - - return NULL; -} - -int -ir_to_mesa_visitor::add_uniform(const char *name, - const glsl_type *type, - ir_constant *constant) -{ - int len; - - if (type->is_vector() || - type->is_scalar()) { - len = type->vector_elements; - } else { - len = type_size(type) * 4; - } - - float *values = NULL; - if (constant && type->is_array()) { - values = (float *)malloc(type->length * 4 * sizeof(float)); - - assert(type->fields.array->is_scalar() || - type->fields.array->is_vector() || - !"FINISHME: uniform array initializers for non-vector"); - - for (unsigned int i = 0; i < type->length; i++) { - ir_constant *element = constant->array_elements[i]; - unsigned int c; - - for (c = 0; c < type->fields.array->vector_elements; c++) { - switch (type->fields.array->base_type) { - case GLSL_TYPE_FLOAT: - values[4 * i + c] = element->value.f[c]; - break; - case GLSL_TYPE_INT: - values[4 * i + c] = element->value.i[c]; - break; - case GLSL_TYPE_UINT: - values[4 * i + c] = element->value.u[c]; - break; - case GLSL_TYPE_BOOL: - values[4 * i + c] = element->value.b[c]; - break; - default: - assert(!"not reached"); - } - } - } - } else if (constant) { - values = (float *)malloc(16 * sizeof(float)); - for (unsigned int i = 0; i < type->components(); i++) { - switch (type->base_type) { - case GLSL_TYPE_FLOAT: - values[i] = constant->value.f[i]; - break; - case GLSL_TYPE_INT: - values[i] = constant->value.i[i]; - break; - case GLSL_TYPE_UINT: - values[i] = constant->value.u[i]; - break; - case GLSL_TYPE_BOOL: - values[i] = constant->value.b[i]; - break; - default: - assert(!"not reached"); - } - } - } - - int loc = _mesa_add_uniform(this->prog->Parameters, - name, - len, - type->gl_type, - values); - free(values); - - return loc; -} - -/* Recursively add all the members of the aggregate uniform as uniform names - * to Mesa, moving those uniforms to our structured temporary. - */ -void -ir_to_mesa_visitor::add_aggregate_uniform(ir_instruction *ir, - const char *name, - const struct glsl_type *type, - ir_constant *constant, - struct ir_to_mesa_dst_reg temp) -{ - int loc; - - if (type->is_record()) { - void *mem_ctx = talloc_new(NULL); - ir_constant *field_constant = NULL; - - if (constant) - field_constant = (ir_constant *)constant->components.get_head(); - - for (unsigned int i = 0; i < type->length; i++) { - const glsl_type *field_type = type->fields.structure[i].type; - - add_aggregate_uniform(ir, - talloc_asprintf(mem_ctx, "%s.%s", name, - type->fields.structure[i].name), - field_type, field_constant, temp); - temp.index += type_size(field_type); - - if (constant) - field_constant = (ir_constant *)field_constant->next; - } - - talloc_free(mem_ctx); - - return; - } - - assert(type->is_vector() || type->is_scalar() || !"FINISHME: other types"); - - loc = add_uniform(name, type, constant); - - ir_to_mesa_src_reg uniform(PROGRAM_UNIFORM, loc, type); - - for (int i = 0; i < type_size(type); i++) { - ir_to_mesa_emit_op1(ir, OPCODE_MOV, temp, uniform); - temp.index++; - uniform.index++; - } -} - - void ir_to_mesa_visitor::visit(ir_dereference_variable *ir) { variable_storage *entry = find_variable_storage(ir->var); - unsigned int loc; if (!entry) { switch (ir->var->mode) { case ir_var_uniform: - entry = get_builtin_matrix_ref(this->mem_ctx, this->prog, ir->var, - NULL); - if (entry) - break; - - /* FINISHME: Fix up uniform name for arrays and things */ - if (ir->var->type->base_type == GLSL_TYPE_SAMPLER) { - /* FINISHME: we whack the location of the var here, which - * is probably not expected. But we need to communicate - * mesa's sampler number to the tex instruction. - */ - int sampler = _mesa_add_sampler(this->prog->Parameters, - ir->var->name, - ir->var->type->gl_type); - map_sampler(ir->var->location, sampler); - - entry = new(mem_ctx) variable_storage(ir->var, PROGRAM_SAMPLER, - sampler); - this->variables.push_tail(entry); - break; - } - - assert(ir->var->type->gl_type != 0 && - ir->var->type->gl_type != GL_INVALID_ENUM); - - /* Oh, the joy of aggregate types in Mesa. Like constants, - * we can only really do vec4s. So, make a temp, chop the - * aggregate up into vec4s, and move those vec4s to the temp. - */ - if (ir->var->type->is_record()) { - ir_to_mesa_src_reg temp = get_temp(ir->var->type); - - entry = new(mem_ctx) variable_storage(ir->var, - temp.file, - temp.index); - this->variables.push_tail(entry); - - add_aggregate_uniform(ir->var, ir->var->name, ir->var->type, - ir->var->constant_value, - ir_to_mesa_dst_reg_from_src(temp)); - break; - } - - loc = add_uniform(ir->var->name, - ir->var->type, - ir->var->constant_value); - - /* Always mark the uniform used at this point. If it isn't - * used, dead code elimination should have nuked the decl already. - */ - this->prog->Parameters->Parameters[loc].Used = GL_TRUE; - - entry = new(mem_ctx) variable_storage(ir->var, PROGRAM_UNIFORM, loc); + entry = new(mem_ctx) variable_storage(ir->var, PROGRAM_UNIFORM, + ir->var->location); this->variables.push_tail(entry); break; case ir_var_in: @@ -1463,7 +1216,7 @@ ir_to_mesa_visitor::visit(ir_dereference_variable *ir) ir->var->location >= VERT_ATTRIB_GENERIC0) { _mesa_add_attribute(prog->Attributes, ir->var->name, - type_size(ir->var->type) * 4, + _mesa_sizeof_glsl_type(ir->var->type->gl_type), ir->var->type->gl_type, ir->var->location - VERT_ATTRIB_GENERIC0); } @@ -1496,57 +1249,12 @@ ir_to_mesa_visitor::visit(ir_dereference_variable *ir) void ir_to_mesa_visitor::visit(ir_dereference_array *ir) { - ir_variable *var = ir->variable_referenced(); ir_constant *index; ir_to_mesa_src_reg src_reg; - ir_dereference_variable *deref_var = ir->array->as_dereference_variable(); int element_size = type_size(ir->type); index = ir->array_index->constant_expression_value(); - if (deref_var && strncmp(deref_var->var->name, - "gl_TextureMatrix", - strlen("gl_TextureMatrix")) == 0) { - struct variable_storage *entry; - - entry = get_builtin_matrix_ref(this->mem_ctx, this->prog, deref_var->var, - ir->array_index); - assert(entry); - - ir_to_mesa_src_reg src_reg(entry->file, entry->index, ir->type); - - if (index) { - src_reg.reladdr = NULL; - } else { - ir_to_mesa_src_reg index_reg = get_temp(glsl_type::float_type); - - ir->array_index->accept(this); - ir_to_mesa_emit_op2(ir, OPCODE_MUL, - ir_to_mesa_dst_reg_from_src(index_reg), - this->result, src_reg_for_float(element_size)); - - src_reg.reladdr = talloc(mem_ctx, ir_to_mesa_src_reg); - memcpy(src_reg.reladdr, &index_reg, sizeof(index_reg)); - } - - this->result = src_reg; - return; - } - - if (strncmp(var->name, "gl_", 3) == 0 && var->mode == ir_var_uniform && - !var->type->is_matrix()) { - ir_dereference_record *record = NULL; - if (ir->array->ir_type == ir_type_dereference_record) - record = (ir_dereference_record *)ir->array; - - assert(index || !"FINISHME: variable-indexed builtin uniform access"); - - this->result = get_builtin_uniform_reg(prog, - var->name, - index->value.i[0], - record ? record->field : NULL); - } - ir->array->accept(this); src_reg = this->result; @@ -1591,17 +1299,6 @@ ir_to_mesa_visitor::visit(ir_dereference_record *ir) unsigned int i; const glsl_type *struct_type = ir->record->type; int offset = 0; - ir_variable *var = ir->record->variable_referenced(); - - if (strncmp(var->name, "gl_", 3) == 0 && var->mode == ir_var_uniform) { - assert(var); - - this->result = get_builtin_uniform_reg(prog, - var->name, - 0, - ir->field); - return; - } ir->record->accept(this); @@ -1618,21 +1315,16 @@ ir_to_mesa_visitor::visit(ir_dereference_record *ir) * We want to be careful in assignment setup to hit the actual storage * instead of potentially using a temporary like we might with the * ir_dereference handler. - * - * Thanks to ir_swizzle_swizzle, and ir_vec_index_to_swizzle, we - * should only see potentially one variable array index of a vector, - * and one swizzle, before getting to actual vec4 storage. So handle - * those, then go use ir_dereference to handle the rest. */ static struct ir_to_mesa_dst_reg -get_assignment_lhs(ir_instruction *ir, ir_to_mesa_visitor *v, - ir_to_mesa_src_reg *r) +get_assignment_lhs(ir_dereference *ir, ir_to_mesa_visitor *v) { - struct ir_to_mesa_dst_reg dst_reg; - ir_swizzle *swiz; - + /* The LHS must be a dereference. If the LHS is a variable indexed array + * access of a vector, it must be separated into a series conditional moves + * before reaching this point (see ir_vec_index_to_cond_assign). + */ + assert(ir->as_dereference()); ir_dereference_array *deref_array = ir->as_dereference_array(); - /* This should have been handled by ir_vec_index_to_cond_assign */ if (deref_array) { assert(!deref_array->array->type->is_vector()); } @@ -1641,38 +1333,7 @@ get_assignment_lhs(ir_instruction *ir, ir_to_mesa_visitor *v, * swizzles in it and write swizzles using writemask, though. */ ir->accept(v); - dst_reg = ir_to_mesa_dst_reg_from_src(v->result); - - if ((swiz = ir->as_swizzle())) { - int swizzles[4] = { - swiz->mask.x, - swiz->mask.y, - swiz->mask.z, - swiz->mask.w - }; - int new_r_swizzle[4]; - int orig_r_swizzle = r->swizzle; - int i; - - for (i = 0; i < 4; i++) { - new_r_swizzle[i] = GET_SWZ(orig_r_swizzle, 0); - } - - dst_reg.writemask = 0; - for (i = 0; i < 4; i++) { - if (i < swiz->mask.num_components) { - dst_reg.writemask |= 1 << swizzles[i]; - new_r_swizzle[swizzles[i]] = GET_SWZ(orig_r_swizzle, i); - } - } - - r->swizzle = MAKE_SWIZZLE4(new_r_swizzle[0], - new_r_swizzle[1], - new_r_swizzle[2], - new_r_swizzle[3]); - } - - return dst_reg; + return ir_to_mesa_dst_reg_from_src(v->result); } void @@ -1685,7 +1346,50 @@ ir_to_mesa_visitor::visit(ir_assignment *ir) ir->rhs->accept(this); r = this->result; - l = get_assignment_lhs(ir->lhs, this, &r); + l = get_assignment_lhs(ir->lhs, this); + + /* FINISHME: This should really set to the correct maximal writemask for each + * FINISHME: component written (in the loops below). This case can only + * FINISHME: occur for matrices, arrays, and structures. + */ + if (ir->write_mask == 0) { + assert(!ir->lhs->type->is_scalar() && !ir->lhs->type->is_vector()); + l.writemask = WRITEMASK_XYZW; + } else if (ir->lhs->type->is_scalar()) { + /* FINISHME: This hack makes writing to gl_FragDepth, which lives in the + * FINISHME: W component of fragment shader output zero, work correctly. + */ + l.writemask = WRITEMASK_XYZW; + } else { + int swizzles[4]; + int first_enabled_chan = 0; + int rhs_chan = 0; + + assert(ir->lhs->type->is_vector()); + l.writemask = ir->write_mask; + + for (int i = 0; i < 4; i++) { + if (l.writemask & (1 << i)) { + first_enabled_chan = GET_SWZ(r.swizzle, i); + break; + } + } + + /* Swizzle a small RHS vector into the channels being written. + * + * glsl ir treats write_mask as dictating how many channels are + * present on the RHS while Mesa IR treats write_mask as just + * showing which channels of the vec4 RHS get written. + */ + for (int i = 0; i < 4; i++) { + if (l.writemask & (1 << i)) + swizzles[i] = GET_SWZ(r.swizzle, rhs_chan++); + else + swizzles[i] = first_enabled_chan; + } + r.swizzle = MAKE_SWIZZLE4(swizzles[0], swizzles[1], + swizzles[2], swizzles[3]); + } assert(l.file != PROGRAM_UNDEFINED); assert(r.file != PROGRAM_UNDEFINED); @@ -1722,7 +1426,7 @@ void ir_to_mesa_visitor::visit(ir_constant *ir) { ir_to_mesa_src_reg src_reg; - GLfloat stack_vals[4]; + GLfloat stack_vals[4] = { 0 }; GLfloat *values = stack_vals; unsigned int i; @@ -1796,6 +1500,7 @@ ir_to_mesa_visitor::visit(ir_constant *ir) } this->result = mat; + return; } src_reg.file = PROGRAM_CONSTANT; @@ -1952,7 +1657,6 @@ ir_to_mesa_visitor::visit(ir_call *ir) this->result = entry->return_reg; } - void ir_to_mesa_visitor::visit(ir_texture *ir) { @@ -2052,21 +1756,20 @@ ir_to_mesa_visitor::visit(ir_texture *ir) if (ir->shadow_comparitor) inst->tex_shadow = GL_TRUE; - ir_dereference_variable *sampler = ir->sampler->as_dereference_variable(); - assert(sampler); /* FINISHME: sampler arrays */ - /* generate the mapping, remove when we generate storage at - * declaration time - */ - sampler->accept(this); + inst->sampler = _mesa_get_sampler_uniform_value(ir->sampler, + this->shader_program, + this->prog); - inst->sampler = get_sampler_number(sampler->var->location); + const glsl_type *sampler_type = ir->sampler->type; - switch (sampler->type->sampler_dimensionality) { + switch (sampler_type->sampler_dimensionality) { case GLSL_SAMPLER_DIM_1D: - inst->tex_target = TEXTURE_1D_INDEX; + inst->tex_target = (sampler_type->sampler_array) + ? TEXTURE_1D_ARRAY_INDEX : TEXTURE_1D_INDEX; break; case GLSL_SAMPLER_DIM_2D: - inst->tex_target = TEXTURE_2D_INDEX; + inst->tex_target = (sampler_type->sampler_array) + ? TEXTURE_2D_ARRAY_INDEX : TEXTURE_2D_INDEX; break; case GLSL_SAMPLER_DIM_3D: inst->tex_target = TEXTURE_3D_INDEX; @@ -2074,8 +1777,14 @@ ir_to_mesa_visitor::visit(ir_texture *ir) case GLSL_SAMPLER_DIM_CUBE: inst->tex_target = TEXTURE_CUBE_INDEX; break; + case GLSL_SAMPLER_DIM_RECT: + inst->tex_target = TEXTURE_RECT_INDEX; + break; + case GLSL_SAMPLER_DIM_BUF: + assert(!"FINISHME: Implement ARB_texture_buffer_object"); + break; default: - assert(!"FINISHME: other texture targets"); + assert(!"Should not get here."); } this->result = result_src; @@ -2084,12 +1793,12 @@ ir_to_mesa_visitor::visit(ir_texture *ir) void ir_to_mesa_visitor::visit(ir_return *ir) { - assert(current_function); - if (ir->get_value()) { ir_to_mesa_dst_reg l; int i; + assert(current_function); + ir->get_value()->accept(this); ir_to_mesa_src_reg r = this->result; @@ -2108,9 +1817,12 @@ ir_to_mesa_visitor::visit(ir_return *ir) void ir_to_mesa_visitor::visit(ir_discard *ir) { + struct gl_fragment_program *fp = (struct gl_fragment_program *)this->prog; + assert(ir->condition == NULL); /* FINISHME */ ir_to_mesa_emit_op0(ir, OPCODE_KIL_NV); + fp->UsesKill = GL_TRUE; } void @@ -2124,7 +1836,7 @@ ir_to_mesa_visitor::visit(ir_if *ir) ir->condition->accept(this); assert(this->result.file != PROGRAM_UNDEFINED); - if (ctx->Shader.EmitCondCodes) { + if (this->options->EmitCondCodes) { cond_inst = (ir_to_mesa_instruction *)this->instructions.get_tail(); /* See if we actually generated any instruction for generating @@ -2165,9 +1877,13 @@ ir_to_mesa_visitor::ir_to_mesa_visitor() result.file = PROGRAM_UNDEFINED; next_temp = 1; next_signature_id = 1; - sampler_map = NULL; - sampler_map_size = 0; current_function = NULL; + mem_ctx = talloc_new(NULL); +} + +ir_to_mesa_visitor::~ir_to_mesa_visitor() +{ + talloc_free(mem_ctx); } static struct prog_src_register @@ -2183,6 +1899,8 @@ mesa_src_reg_from_ir_src_reg(ir_to_mesa_src_reg reg) mesa_reg.Negate = reg.negate; mesa_reg.Abs = 0; mesa_reg.HasIndex2 = GL_FALSE; + mesa_reg.RelAddr2 = 0; + mesa_reg.Index2 = 0; return mesa_reg; } @@ -2214,8 +1932,8 @@ set_branchtargets(ir_to_mesa_visitor *v, } } - if_stack = (int *)calloc(if_count, sizeof(*if_stack)); - loop_stack = (int *)calloc(loop_count, sizeof(*loop_stack)); + if_stack = talloc_zero_array(v->mem_ctx, int, if_count); + loop_stack = talloc_zero_array(v->mem_ctx, int, loop_count); for (i = 0; i < num_instructions; i++) { switch (mesa_instructions[i].Opcode) { @@ -2267,8 +1985,6 @@ set_branchtargets(ir_to_mesa_visitor *v, break; } } - - free(if_stack); } static void @@ -2305,128 +2021,243 @@ print_program(struct prog_instruction *mesa_instructions, } static void -mark_input(struct gl_program *prog, - int index, - GLboolean reladdr) +count_resources(struct gl_program *prog) { - prog->InputsRead |= BITFIELD64_BIT(index); - int i; + unsigned int i; + + prog->SamplersUsed = 0; + + for (i = 0; i < prog->NumInstructions; i++) { + struct prog_instruction *inst = &prog->Instructions[i]; - if (reladdr) { - if (index >= FRAG_ATTRIB_TEX0 && index <= FRAG_ATTRIB_TEX7) { - for (i = 0; i < 8; i++) { - prog->InputsRead |= BITFIELD64_BIT(FRAG_ATTRIB_TEX0 + i); + if (_mesa_is_tex_instruction(inst->Opcode)) { + prog->SamplerTargets[inst->TexSrcUnit] = + (gl_texture_index)inst->TexSrcTarget; + prog->SamplersUsed |= 1 << inst->TexSrcUnit; + if (inst->TexShadow) { + prog->ShadowSamplers |= 1 << inst->TexSrcUnit; } - } else { - assert(!"FINISHME: Mark InputsRead for varying arrays"); } } + + _mesa_update_shader_textures_used(prog); } -static void -mark_output(struct gl_program *prog, - int index, - GLboolean reladdr) +struct uniform_sort { + struct gl_uniform *u; + int pos; +}; + +/* The shader_program->Uniforms list is almost sorted in increasing + * uniform->{Frag,Vert}Pos locations, but not quite when there are + * uniforms shared between targets. We need to add parameters in + * increasing order for the targets. + */ +static int +sort_uniforms(const void *a, const void *b) { - prog->OutputsWritten |= BITFIELD64_BIT(index); - int i; + struct uniform_sort *u1 = (struct uniform_sort *)a; + struct uniform_sort *u2 = (struct uniform_sort *)b; - if (reladdr) { - if (index >= VERT_RESULT_TEX0 && index <= VERT_RESULT_TEX7) { - for (i = 0; i < 8; i++) { - prog->OutputsWritten |= BITFIELD64_BIT(FRAG_ATTRIB_TEX0 + i); - } - } else { - assert(!"FINISHME: Mark OutputsWritten for varying arrays"); - } - } + return u1->pos - u2->pos; } +/* Add the uniforms to the parameters. The linker chose locations + * in our parameters lists (which weren't created yet), which the + * uniforms code will use to poke values into our parameters list + * when uniforms are updated. + */ static void -count_resources(struct gl_program *prog) +add_uniforms_to_parameters_list(struct gl_shader_program *shader_program, + struct gl_shader *shader, + struct gl_program *prog) { unsigned int i; + unsigned int next_sampler = 0, num_uniforms = 0; + struct uniform_sort *sorted_uniforms; - prog->InputsRead = 0; - prog->OutputsWritten = 0; - prog->SamplersUsed = 0; + sorted_uniforms = talloc_array(NULL, struct uniform_sort, + shader_program->Uniforms->NumUniforms); - for (i = 0; i < prog->NumInstructions; i++) { - struct prog_instruction *inst = &prog->Instructions[i]; - unsigned int reg; + for (i = 0; i < shader_program->Uniforms->NumUniforms; i++) { + struct gl_uniform *uniform = shader_program->Uniforms->Uniforms + i; + int parameter_index = -1; - switch (inst->DstReg.File) { - case PROGRAM_OUTPUT: - mark_output(prog, inst->DstReg.Index, inst->DstReg.RelAddr); + switch (shader->Type) { + case GL_VERTEX_SHADER: + parameter_index = uniform->VertPos; break; - case PROGRAM_INPUT: - mark_input(prog, inst->DstReg.Index, inst->DstReg.RelAddr); + case GL_FRAGMENT_SHADER: + parameter_index = uniform->FragPos; break; - default: + case GL_GEOMETRY_SHADER: + parameter_index = uniform->GeomPos; break; } - for (reg = 0; reg < _mesa_num_inst_src_regs(inst->Opcode); reg++) { - switch (inst->SrcReg[reg].File) { - case PROGRAM_OUTPUT: - mark_output(prog, inst->SrcReg[reg].Index, - inst->SrcReg[reg].RelAddr); - break; - case PROGRAM_INPUT: - mark_input(prog, inst->SrcReg[reg].Index, inst->SrcReg[reg].RelAddr); - break; - default: - break; - } + /* Only add uniforms used in our target. */ + if (parameter_index != -1) { + sorted_uniforms[num_uniforms].pos = parameter_index; + sorted_uniforms[num_uniforms].u = uniform; + num_uniforms++; } + } - /* Instead of just using the uniform's value to map to a - * sampler, Mesa first allocates a separate number for the - * sampler (_mesa_add_sampler), then we reindex it down to a - * small integer (sampler_map[], SamplersUsed), then that gets - * mapped to the uniform's value, and we get an actual sampler. - */ - if (_mesa_is_tex_instruction(inst->Opcode)) { - prog->SamplerTargets[inst->TexSrcUnit] = - (gl_texture_index)inst->TexSrcTarget; - prog->SamplersUsed |= 1 << inst->TexSrcUnit; - if (inst->TexShadow) { - prog->ShadowSamplers |= 1 << inst->TexSrcUnit; + qsort(sorted_uniforms, num_uniforms, sizeof(struct uniform_sort), + sort_uniforms); + + for (i = 0; i < num_uniforms; i++) { + struct gl_uniform *uniform = sorted_uniforms[i].u; + int parameter_index = sorted_uniforms[i].pos; + const glsl_type *type = uniform->Type; + unsigned int size; + + if (type->is_vector() || + type->is_scalar()) { + size = type->vector_elements; + } else { + size = type_size(type) * 4; + } + + gl_register_file file; + if (type->is_sampler() || + (type->is_array() && type->fields.array->is_sampler())) { + file = PROGRAM_SAMPLER; + } else { + file = PROGRAM_UNIFORM; + } + + GLint index = _mesa_lookup_parameter_index(prog->Parameters, -1, + uniform->Name); + + if (index < 0) { + index = _mesa_add_parameter(prog->Parameters, file, + uniform->Name, size, type->gl_type, + NULL, NULL, 0x0); + + /* Sampler uniform values are stored in prog->SamplerUnits, + * and the entry in that array is selected by this index we + * store in ParameterValues[]. + */ + if (file == PROGRAM_SAMPLER) { + for (unsigned int j = 0; j < size / 4; j++) + prog->Parameters->ParameterValues[index + j][0] = next_sampler++; + } + + /* The location chosen in the Parameters list here (returned + * from _mesa_add_uniform) has to match what the linker chose. + */ + if (index != parameter_index) { + fail_link(shader_program, "Allocation of uniform `%s' to target " + "failed (%d vs %d)\n", + uniform->Name, index, parameter_index); } } } - _mesa_update_shader_textures_used(prog); + talloc_free(sorted_uniforms); } -/* Each stage has some uniforms in its Parameters list. The Uniforms - * list for the linked shader program has a pointer to these uniforms - * in each of the stage's Parameters list, so that their values can be - * updated when a uniform is set. - */ static void -link_uniforms_to_shared_uniform_list(struct gl_uniform_list *uniforms, - struct gl_program *prog) +set_uniform_initializer(GLcontext *ctx, void *mem_ctx, + struct gl_shader_program *shader_program, + const char *name, const glsl_type *type, + ir_constant *val) { - unsigned int i; + if (type->is_record()) { + ir_constant *field_constant; - for (i = 0; i < prog->Parameters->NumParameters; i++) { - const struct gl_program_parameter *p = prog->Parameters->Parameters + i; + field_constant = (ir_constant *)val->components.get_head(); - if (p->Type == PROGRAM_UNIFORM || p->Type == PROGRAM_SAMPLER) { - struct gl_uniform *uniform = - _mesa_append_uniform(uniforms, p->Name, prog->Target, i); - if (uniform) - uniform->Initialized = p->Initialized; + for (unsigned int i = 0; i < type->length; i++) { + const glsl_type *field_type = type->fields.structure[i].type; + const char *field_name = talloc_asprintf(mem_ctx, "%s.%s", name, + type->fields.structure[i].name); + set_uniform_initializer(ctx, mem_ctx, shader_program, field_name, + field_type, field_constant); + field_constant = (ir_constant *)field_constant->next; + } + return; + } + + int loc = _mesa_get_uniform_location(ctx, shader_program, name); + + if (loc == -1) { + fail_link(shader_program, + "Couldn't find uniform for initializer %s\n", name); + return; + } + + for (unsigned int i = 0; i < (type->is_array() ? type->length : 1); i++) { + ir_constant *element; + const glsl_type *element_type; + if (type->is_array()) { + element = val->array_elements[i]; + element_type = type->fields.array; + } else { + element = val; + element_type = type; + } + + void *values; + + if (element_type->base_type == GLSL_TYPE_BOOL) { + int *conv = talloc_array(mem_ctx, int, element_type->components()); + for (unsigned int j = 0; j < element_type->components(); j++) { + conv[j] = element->value.b[j]; + } + values = (void *)conv; + element_type = glsl_type::get_instance(GLSL_TYPE_INT, + element_type->vector_elements, + 1); + } else { + values = &element->value; + } + + if (element_type->is_matrix()) { + _mesa_uniform_matrix(ctx, shader_program, + element_type->matrix_columns, + element_type->vector_elements, + loc, 1, GL_FALSE, (GLfloat *)values); + loc += element_type->matrix_columns; + } else { + _mesa_uniform(ctx, shader_program, loc, element_type->matrix_columns, + values, element_type->gl_type); + loc += type_size(element_type); } } } +static void +set_uniform_initializers(GLcontext *ctx, + struct gl_shader_program *shader_program) +{ + void *mem_ctx = NULL; + + for (unsigned int i = 0; i < shader_program->_NumLinkedShaders; i++) { + struct gl_shader *shader = shader_program->_LinkedShaders[i]; + foreach_iter(exec_list_iterator, iter, *shader->ir) { + ir_instruction *ir = (ir_instruction *)iter.get(); + ir_variable *var = ir->as_variable(); + + if (!var || var->mode != ir_var_uniform || !var->constant_value) + continue; + + if (!mem_ctx) + mem_ctx = talloc_new(NULL); + + set_uniform_initializer(ctx, mem_ctx, shader_program, var->name, + var->type, var->constant_value); + } + } + + talloc_free(mem_ctx); +} + struct gl_program * get_mesa_program(GLcontext *ctx, struct gl_shader_program *shader_program, struct gl_shader *shader) { - void *mem_ctx = shader_program; ir_to_mesa_visitor v; struct prog_instruction *mesa_instructions, *mesa_inst; ir_instruction **mesa_instruction_annotation; @@ -2435,6 +2266,8 @@ get_mesa_program(GLcontext *ctx, struct gl_shader_program *shader_program, GLenum target; const char *target_string; GLboolean progress; + struct gl_shader_compiler_options *options = + &ctx->ShaderCompilerOptions[_mesa_shader_type_to_index(shader->Type)]; switch (shader->Type) { case GL_VERTEX_SHADER: @@ -2447,7 +2280,7 @@ get_mesa_program(GLcontext *ctx, struct gl_shader_program *shader_program, break; default: assert(!"should not be reached"); - break; + return NULL; } validate_ir_tree(shader->ir); @@ -2460,8 +2293,10 @@ get_mesa_program(GLcontext *ctx, struct gl_shader_program *shader_program, prog->Attributes = _mesa_new_parameter_list(); v.ctx = ctx; v.prog = prog; + v.shader_program = shader_program; + v.options = options; - v.mem_ctx = talloc_new(NULL); + add_uniforms_to_parameters_list(shader_program, shader, prog); /* Emit Mesa IR for main(). */ visit_exec_list(shader->ir, &v); @@ -2506,7 +2341,7 @@ get_mesa_program(GLcontext *ctx, struct gl_shader_program *shader_program, mesa_instructions = (struct prog_instruction *)calloc(num_instructions, sizeof(*mesa_instructions)); - mesa_instruction_annotation = talloc_array(mem_ctx, ir_instruction *, + mesa_instruction_annotation = talloc_array(v.mem_ctx, ir_instruction *, num_instructions); mesa_inst = mesa_instructions; @@ -2529,11 +2364,16 @@ get_mesa_program(GLcontext *ctx, struct gl_shader_program *shader_program, mesa_inst->TexShadow = inst->tex_shadow; mesa_instruction_annotation[i] = inst->ir; - if (ctx->Shader.EmitNoIfs && mesa_inst->Opcode == OPCODE_IF) { - shader_program->InfoLog = - talloc_asprintf_append(shader_program->InfoLog, - "Couldn't flatten if statement\n"); - shader_program->LinkStatus = false; + /* Set IndirectRegisterFiles. */ + if (mesa_inst->DstReg.RelAddr) + prog->IndirectRegisterFiles |= 1 << mesa_inst->DstReg.File; + + for (unsigned src = 0; src < 3; src++) + if (mesa_inst->SrcReg[src].RelAddr) + prog->IndirectRegisterFiles |= 1 << mesa_inst->SrcReg[src].File; + + if (options->EmitNoIfs && mesa_inst->Opcode == OPCODE_IF) { + fail_link(shader_program, "Couldn't flatten if statement\n"); } switch (mesa_inst->Opcode) { @@ -2559,8 +2399,16 @@ get_mesa_program(GLcontext *ctx, struct gl_shader_program *shader_program, } set_branchtargets(&v, mesa_instructions, num_instructions); + if (ctx->Shader.Flags & GLSL_DUMP) { - printf("Mesa %s program:\n", target_string); + printf("\n"); + printf("GLSL IR for linked %s program %d:\n", target_string, + shader_program->Name); + _mesa_print_ir(shader->ir, NULL); + printf("\n"); + printf("\n"); + printf("Mesa IR for linked %s program %d:\n", target_string, + shader_program->Name); print_program(mesa_instructions, mesa_instruction_annotation, num_instructions); } @@ -2568,6 +2416,9 @@ get_mesa_program(GLcontext *ctx, struct gl_shader_program *shader_program, prog->Instructions = mesa_instructions; prog->NumInstructions = num_instructions; + do_set_program_inouts(shader->ir, prog); + count_resources(prog); + _mesa_reference_program(ctx, &shader->Program, prog); if ((ctx->Shader.Flags & GLSL_NO_OPT) == 0) { @@ -2578,6 +2429,92 @@ get_mesa_program(GLcontext *ctx, struct gl_shader_program *shader_program, } extern "C" { +GLboolean +_mesa_ir_compile_shader(GLcontext *ctx, struct gl_shader *shader) +{ + assert(shader->CompileStatus); + (void) ctx; + + return GL_TRUE; +} + +GLboolean +_mesa_ir_link_shader(GLcontext *ctx, struct gl_shader_program *prog) +{ + assert(prog->LinkStatus); + + for (unsigned i = 0; i < prog->_NumLinkedShaders; i++) { + bool progress; + exec_list *ir = prog->_LinkedShaders[i]->ir; + struct gl_shader_compiler_options *options = + &ctx->ShaderCompilerOptions[_mesa_shader_type_to_index(prog->_LinkedShaders[i]->Type)]; + + do { + progress = false; + + /* Lowering */ + do_mat_op_to_vec(ir); + do_mod_to_fract(ir); + do_div_to_mul_rcp(ir); + do_explog_to_explog2(ir); + + progress = do_lower_jumps(ir, true, true, options->EmitNoMainReturn, options->EmitNoCont, options->EmitNoLoops) || progress; + + progress = do_common_optimization(ir, true, options->MaxUnrollIterations) || progress; + + if (options->EmitNoIfs) + progress = do_if_to_cond_assign(ir) || progress; + + if (options->EmitNoNoise) + progress = lower_noise(ir) || progress; + + /* If there are forms of indirect addressing that the driver + * cannot handle, perform the lowering pass. + */ + if (options->EmitNoIndirectInput || options->EmitNoIndirectOutput + || options->EmitNoIndirectTemp || options->EmitNoIndirectUniform) + progress = + lower_variable_index_to_cond_assign(ir, + options->EmitNoIndirectInput, + options->EmitNoIndirectOutput, + options->EmitNoIndirectTemp, + options->EmitNoIndirectUniform) + || progress; + + progress = do_vec_index_to_cond_assign(ir) || progress; + } while (progress); + + validate_ir_tree(ir); + } + + for (unsigned i = 0; i < prog->_NumLinkedShaders; i++) { + struct gl_program *linked_prog; + bool ok = true; + + linked_prog = get_mesa_program(ctx, prog, prog->_LinkedShaders[i]); + + switch (prog->_LinkedShaders[i]->Type) { + case GL_VERTEX_SHADER: + _mesa_reference_vertprog(ctx, &prog->VertexProgram, + (struct gl_vertex_program *)linked_prog); + ok = ctx->Driver.ProgramStringNotify(ctx, GL_VERTEX_PROGRAM_ARB, + linked_prog); + break; + case GL_FRAGMENT_SHADER: + _mesa_reference_fragprog(ctx, &prog->FragmentProgram, + (struct gl_fragment_program *)linked_prog); + ok = ctx->Driver.ProgramStringNotify(ctx, GL_FRAGMENT_PROGRAM_ARB, + linked_prog); + break; + } + if (!ok) { + return GL_FALSE; + } + _mesa_reference_program(ctx, &linked_prog, NULL); + } + + return GL_TRUE; +} void _mesa_glsl_compile_shader(GLcontext *ctx, struct gl_shader *shader) @@ -2586,8 +2523,21 @@ _mesa_glsl_compile_shader(GLcontext *ctx, struct gl_shader *shader) new(shader) _mesa_glsl_parse_state(ctx, shader->Type, shader); const char *source = shader->Source; + /* Check if the user called glCompileShader without first calling + * glShaderSource. This should fail to compile, but not raise a GL_ERROR. + */ + if (source == NULL) { + shader->CompileStatus = GL_FALSE; + return; + } + state->error = preprocess(state, &source, &state->info_log, - &ctx->Extensions); + &ctx->Extensions, ctx->API); + + if (ctx->Shader.Flags & GLSL_DUMP) { + printf("GLSL source for shader %d:\n", shader->Name); + printf("%s\n", shader->Source); + } if (!state->error) { _mesa_glsl_lexer_ctor(state, source); @@ -2595,6 +2545,7 @@ _mesa_glsl_compile_shader(GLcontext *ctx, struct gl_shader *shader) _mesa_glsl_lexer_dtor(state); } + talloc_free(shader->ir); shader->ir = new(shader) exec_list; if (!state->error && !state->translation_unit.is_empty()) _mesa_ast_to_hir(shader->ir, state); @@ -2602,37 +2553,11 @@ _mesa_glsl_compile_shader(GLcontext *ctx, struct gl_shader *shader) if (!state->error && !shader->ir->is_empty()) { validate_ir_tree(shader->ir); - /* Lowering */ - do_mat_op_to_vec(shader->ir); - do_mod_to_fract(shader->ir); - do_div_to_mul_rcp(shader->ir); - - /* Optimization passes */ - bool progress; - do { - progress = false; - - progress = do_function_inlining(shader->ir) || progress; - progress = do_if_simplification(shader->ir) || progress; - progress = do_copy_propagation(shader->ir) || progress; - progress = do_dead_code_local(shader->ir) || progress; - progress = do_dead_code_unlinked(shader->ir) || progress; - progress = do_tree_grafting(shader->ir) || progress; - progress = do_constant_variable_unlinked(shader->ir) || progress; - progress = do_constant_folding(shader->ir) || progress; - progress = do_algebraic(shader->ir) || progress; - progress = do_if_return(shader->ir) || progress; - if (ctx->Shader.EmitNoIfs) - progress = do_if_to_cond_assign(shader->ir) || progress; - - progress = do_vec_index_to_swizzle(shader->ir) || progress; - /* Do this one after the previous to let the easier pass handle - * constant vector indexing. - */ - progress = do_vec_index_to_cond_assign(shader->ir) || progress; - - progress = do_swizzle_swizzle(shader->ir) || progress; - } while (progress); + /* Do some optimization at compile time to reduce shader IR size + * and reduce later work if the same shader is linked multiple times + */ + while (do_common_optimization(shader->ir, false, 32)) + ; validate_ir_tree(shader->ir); } @@ -2650,11 +2575,30 @@ _mesa_glsl_compile_shader(GLcontext *ctx, struct gl_shader *shader) _mesa_write_shader_to_file(shader); } + if (ctx->Shader.Flags & GLSL_DUMP) { + if (shader->CompileStatus) { + printf("GLSL IR for shader %d:\n", shader->Name); + _mesa_print_ir(shader->ir, NULL); + printf("\n\n"); + } else { + printf("GLSL shader %d failed to compile.\n", shader->Name); + } + if (shader->InfoLog && shader->InfoLog[0] != 0) { + printf("GLSL shader %d info log:\n", shader->Name); + printf("%s\n", shader->InfoLog); + } + } + /* Retain any live IR, but trash the rest. */ - reparent_ir(shader->ir, shader); + reparent_ir(shader->ir, shader->ir); talloc_free(state); - } + + if (shader->CompileStatus) { + if (!ctx->Driver.CompileShader(ctx, shader)) + shader->CompileStatus = GL_FALSE; + } +} void _mesa_glsl_link_shader(GLcontext *ctx, struct gl_shader_program *prog) @@ -2667,9 +2611,7 @@ _mesa_glsl_link_shader(GLcontext *ctx, struct gl_shader_program *prog) for (i = 0; i < prog->NumShaders; i++) { if (!prog->Shaders[i]->CompileStatus) { - prog->InfoLog = - talloc_asprintf_append(prog->InfoLog, - "linking with uncompiled shader"); + fail_link(prog, "linking with uncompiled shader"); prog->LinkStatus = GL_FALSE; } } @@ -2679,43 +2621,25 @@ _mesa_glsl_link_shader(GLcontext *ctx, struct gl_shader_program *prog) _mesa_reference_fragprog(ctx, &prog->FragmentProgram, NULL); if (prog->LinkStatus) { - link_shaders(prog); - - /* We don't use the linker's uniforms list, and cook up our own at - * generate time. - */ - free(prog->Uniforms); - prog->Uniforms = _mesa_new_uniform_list(); + link_shaders(ctx, prog); } if (prog->LinkStatus) { - for (i = 0; i < prog->_NumLinkedShaders; i++) { - struct gl_program *linked_prog; - bool ok = true; - - linked_prog = get_mesa_program(ctx, prog, - prog->_LinkedShaders[i]); - count_resources(linked_prog); - - link_uniforms_to_shared_uniform_list(prog->Uniforms, linked_prog); - - switch (prog->_LinkedShaders[i]->Type) { - case GL_VERTEX_SHADER: - _mesa_reference_vertprog(ctx, &prog->VertexProgram, - (struct gl_vertex_program *)linked_prog); - ok = ctx->Driver.ProgramStringNotify(ctx, GL_VERTEX_PROGRAM_ARB, - linked_prog); - break; - case GL_FRAGMENT_SHADER: - _mesa_reference_fragprog(ctx, &prog->FragmentProgram, - (struct gl_fragment_program *)linked_prog); - ok = ctx->Driver.ProgramStringNotify(ctx, GL_FRAGMENT_PROGRAM_ARB, - linked_prog); - break; - } - if (!ok) { - prog->LinkStatus = GL_FALSE; - } + if (!ctx->Driver.LinkShader(ctx, prog)) { + prog->LinkStatus = GL_FALSE; + } + } + + set_uniform_initializers(ctx, prog); + + if (ctx->Shader.Flags & GLSL_DUMP) { + if (!prog->LinkStatus) { + printf("GLSL shader program %d failed to link\n", prog->Name); + } + + if (prog->InfoLog && prog->InfoLog[0] != 0) { + printf("GLSL shader program %d info log:\n", prog->Name); + printf("%s\n", prog->InfoLog); } } }