From 2039567395da75a3ae318fb82738cdcf2caa05a1 Mon Sep 17 00:00:00 2001 From: marha Date: Wed, 30 Mar 2011 06:43:04 +0000 Subject: mesa git update 30 Mar 2011 --- mesalib/src/mesa/program/arbprogparse.c | 441 +- mesalib/src/mesa/program/ir_to_mesa.cpp | 6641 ++++++++++++++--------------- mesalib/src/mesa/program/prog_statevars.c | 2390 ++++++----- mesalib/src/mesa/program/prog_statevars.h | 295 +- mesalib/src/mesa/program/programopt.c | 1339 +++--- mesalib/src/mesa/program/programopt.h | 106 +- 6 files changed, 5610 insertions(+), 5602 deletions(-) (limited to 'mesalib/src/mesa/program') diff --git a/mesalib/src/mesa/program/arbprogparse.c b/mesalib/src/mesa/program/arbprogparse.c index ebbc195bc..7f778c3c3 100644 --- a/mesalib/src/mesa/program/arbprogparse.c +++ b/mesalib/src/mesa/program/arbprogparse.c @@ -1,219 +1,222 @@ -/* - * Mesa 3-D graphics library - * Version: 7.1 - * - * Copyright (C) 1999-2008 Brian Paul All Rights Reserved. - * - * 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 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 - * BRIAN PAUL 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. - */ - -#define DEBUG_PARSING 0 - -/** - * \file arbprogparse.c - * ARB_*_program parser core - * \author Karl Rasche - */ - -/** -Notes on program parameters, etc. - -The instructions we emit will use six kinds of source registers: - - PROGRAM_INPUT - input registers - PROGRAM_TEMPORARY - temp registers - PROGRAM_ADDRESS - address/indirect register - PROGRAM_SAMPLER - texture sampler - PROGRAM_CONSTANT - indexes into program->Parameters, a known constant/literal - PROGRAM_STATE_VAR - indexes into program->Parameters, and may actually be: - + a state variable, like "state.fog.color", or - + a pointer to a "program.local[k]" parameter, or - + a pointer to a "program.env[k]" parameter - -Basically, all the program.local[] and program.env[] values will get mapped -into the unified gl_program->Parameters array. This solves the problem of -having three separate program parameter arrays. -*/ - - -#include "main/glheader.h" -#include "main/imports.h" -#include "main/context.h" -#include "main/mtypes.h" -#include "arbprogparse.h" -#include "programopt.h" -#include "prog_parameter.h" -#include "prog_statevars.h" -#include "prog_instruction.h" -#include "program_parser.h" - - -void -_mesa_parse_arb_fragment_program(struct gl_context* ctx, GLenum target, - const GLvoid *str, GLsizei len, - struct gl_fragment_program *program) -{ - struct gl_program prog; - struct asm_parser_state state; - GLuint i; - - ASSERT(target == GL_FRAGMENT_PROGRAM_ARB); - - memset(&prog, 0, sizeof(prog)); - memset(&state, 0, sizeof(state)); - state.prog = &prog; - - if (!_mesa_parse_arb_program(ctx, target, (const GLubyte*) str, len, - &state)) { - /* Error in the program. Just return. */ - return; - } - - if (program->Base.String != NULL) - free(program->Base.String); - - /* Copy the relevant contents of the arb_program struct into the - * fragment_program struct. - */ - program->Base.String = prog.String; - program->Base.NumInstructions = prog.NumInstructions; - program->Base.NumTemporaries = prog.NumTemporaries; - program->Base.NumParameters = prog.NumParameters; - program->Base.NumAttributes = prog.NumAttributes; - program->Base.NumAddressRegs = prog.NumAddressRegs; - program->Base.NumNativeInstructions = prog.NumNativeInstructions; - program->Base.NumNativeTemporaries = prog.NumNativeTemporaries; - program->Base.NumNativeParameters = prog.NumNativeParameters; - program->Base.NumNativeAttributes = prog.NumNativeAttributes; - program->Base.NumNativeAddressRegs = prog.NumNativeAddressRegs; - program->Base.NumAluInstructions = prog.NumAluInstructions; - program->Base.NumTexInstructions = prog.NumTexInstructions; - program->Base.NumTexIndirections = prog.NumTexIndirections; - program->Base.NumNativeAluInstructions = prog.NumAluInstructions; - program->Base.NumNativeTexInstructions = prog.NumTexInstructions; - program->Base.NumNativeTexIndirections = prog.NumTexIndirections; - program->Base.InputsRead = prog.InputsRead; - program->Base.OutputsWritten = prog.OutputsWritten; - program->Base.IndirectRegisterFiles = prog.IndirectRegisterFiles; - for (i = 0; i < MAX_TEXTURE_IMAGE_UNITS; i++) { - program->Base.TexturesUsed[i] = prog.TexturesUsed[i]; - if (prog.TexturesUsed[i]) - program->Base.SamplersUsed |= (1 << i); - } - program->Base.ShadowSamplers = prog.ShadowSamplers; - switch (state.option.Fog) { - case OPTION_FOG_EXP: program->FogOption = GL_EXP; break; - case OPTION_FOG_EXP2: program->FogOption = GL_EXP2; break; - case OPTION_FOG_LINEAR: program->FogOption = GL_LINEAR; break; - default: program->FogOption = GL_NONE; break; - } - program->OriginUpperLeft = state.option.OriginUpperLeft; - program->PixelCenterInteger = state.option.PixelCenterInteger; - - program->UsesKill = state.fragment.UsesKill; - - if (program->FogOption) - program->Base.InputsRead |= FRAG_BIT_FOGC; - - if (program->Base.Instructions) - free(program->Base.Instructions); - program->Base.Instructions = prog.Instructions; - - if (program->Base.Parameters) - _mesa_free_parameter_list(program->Base.Parameters); - program->Base.Parameters = prog.Parameters; - - /* Append fog instructions now if the program has "OPTION ARB_fog_exp" - * or similar. We used to leave this up to drivers, but it appears - * there's no hardware that wants to do fog in a discrete stage separate - * from the fragment shader. - */ - if (program->FogOption != GL_NONE) { - _mesa_append_fog_code(ctx, program); - program->FogOption = GL_NONE; - } - -#if DEBUG_FP - printf("____________Fragment program %u ________\n", program->Base.Id); - _mesa_print_program(&program->Base); -#endif -} - - - -/** - * Parse the vertex program string. If success, update the given - * vertex_program object with the new program. Else, leave the vertex_program - * object unchanged. - */ -void -_mesa_parse_arb_vertex_program(struct gl_context *ctx, GLenum target, - const GLvoid *str, GLsizei len, - struct gl_vertex_program *program) -{ - struct gl_program prog; - struct asm_parser_state state; - - ASSERT(target == GL_VERTEX_PROGRAM_ARB); - - memset(&prog, 0, sizeof(prog)); - memset(&state, 0, sizeof(state)); - state.prog = &prog; - - if (!_mesa_parse_arb_program(ctx, target, (const GLubyte*) str, len, - &state)) { - _mesa_error(ctx, GL_INVALID_OPERATION, "glProgramString(bad program)"); - return; - } - - if (program->Base.String != NULL) - free(program->Base.String); - - /* Copy the relevant contents of the arb_program struct into the - * vertex_program struct. - */ - program->Base.String = prog.String; - program->Base.NumInstructions = prog.NumInstructions; - program->Base.NumTemporaries = prog.NumTemporaries; - program->Base.NumParameters = prog.NumParameters; - program->Base.NumAttributes = prog.NumAttributes; - program->Base.NumAddressRegs = prog.NumAddressRegs; - program->Base.NumNativeInstructions = prog.NumNativeInstructions; - program->Base.NumNativeTemporaries = prog.NumNativeTemporaries; - program->Base.NumNativeParameters = prog.NumNativeParameters; - program->Base.NumNativeAttributes = prog.NumNativeAttributes; - program->Base.NumNativeAddressRegs = prog.NumNativeAddressRegs; - program->Base.InputsRead = prog.InputsRead; - program->Base.OutputsWritten = prog.OutputsWritten; - program->Base.IndirectRegisterFiles = prog.IndirectRegisterFiles; - program->IsPositionInvariant = (state.option.PositionInvariant) - ? GL_TRUE : GL_FALSE; - - if (program->Base.Instructions) - free(program->Base.Instructions); - program->Base.Instructions = prog.Instructions; - - if (program->Base.Parameters) - _mesa_free_parameter_list(program->Base.Parameters); - program->Base.Parameters = prog.Parameters; - -#if DEBUG_VP - printf("____________Vertex program %u __________\n", program->Base.Id); - _mesa_print_program(&program->Base); -#endif -} +/* + * Mesa 3-D graphics library + * Version: 7.1 + * + * Copyright (C) 1999-2008 Brian Paul All Rights Reserved. + * + * 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 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 + * BRIAN PAUL 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. + */ + +#define DEBUG_PARSING 0 + +/** + * \file arbprogparse.c + * ARB_*_program parser core + * \author Karl Rasche + */ + +/** +Notes on program parameters, etc. + +The instructions we emit will use six kinds of source registers: + + PROGRAM_INPUT - input registers + PROGRAM_TEMPORARY - temp registers + PROGRAM_ADDRESS - address/indirect register + PROGRAM_SAMPLER - texture sampler + PROGRAM_CONSTANT - indexes into program->Parameters, a known constant/literal + PROGRAM_STATE_VAR - indexes into program->Parameters, and may actually be: + + a state variable, like "state.fog.color", or + + a pointer to a "program.local[k]" parameter, or + + a pointer to a "program.env[k]" parameter + +Basically, all the program.local[] and program.env[] values will get mapped +into the unified gl_program->Parameters array. This solves the problem of +having three separate program parameter arrays. +*/ + + +#include "main/glheader.h" +#include "main/imports.h" +#include "main/context.h" +#include "main/mtypes.h" +#include "arbprogparse.h" +#include "programopt.h" +#include "prog_parameter.h" +#include "prog_statevars.h" +#include "prog_instruction.h" +#include "program_parser.h" + + +void +_mesa_parse_arb_fragment_program(struct gl_context* ctx, GLenum target, + const GLvoid *str, GLsizei len, + struct gl_fragment_program *program) +{ + struct gl_program prog; + struct asm_parser_state state; + GLuint i; + + ASSERT(target == GL_FRAGMENT_PROGRAM_ARB); + + memset(&prog, 0, sizeof(prog)); + memset(&state, 0, sizeof(state)); + state.prog = &prog; + + if (!_mesa_parse_arb_program(ctx, target, (const GLubyte*) str, len, + &state)) { + /* Error in the program. Just return. */ + return; + } + + if (program->Base.String != NULL) + free(program->Base.String); + + /* Copy the relevant contents of the arb_program struct into the + * fragment_program struct. + */ + program->Base.String = prog.String; + program->Base.NumInstructions = prog.NumInstructions; + program->Base.NumTemporaries = prog.NumTemporaries; + program->Base.NumParameters = prog.NumParameters; + program->Base.NumAttributes = prog.NumAttributes; + program->Base.NumAddressRegs = prog.NumAddressRegs; + program->Base.NumNativeInstructions = prog.NumNativeInstructions; + program->Base.NumNativeTemporaries = prog.NumNativeTemporaries; + program->Base.NumNativeParameters = prog.NumNativeParameters; + program->Base.NumNativeAttributes = prog.NumNativeAttributes; + program->Base.NumNativeAddressRegs = prog.NumNativeAddressRegs; + program->Base.NumAluInstructions = prog.NumAluInstructions; + program->Base.NumTexInstructions = prog.NumTexInstructions; + program->Base.NumTexIndirections = prog.NumTexIndirections; + program->Base.NumNativeAluInstructions = prog.NumAluInstructions; + program->Base.NumNativeTexInstructions = prog.NumTexInstructions; + program->Base.NumNativeTexIndirections = prog.NumTexIndirections; + program->Base.InputsRead = prog.InputsRead; + program->Base.OutputsWritten = prog.OutputsWritten; + program->Base.IndirectRegisterFiles = prog.IndirectRegisterFiles; + for (i = 0; i < MAX_TEXTURE_IMAGE_UNITS; i++) { + program->Base.TexturesUsed[i] = prog.TexturesUsed[i]; + if (prog.TexturesUsed[i]) + program->Base.SamplersUsed |= (1 << i); + } + program->Base.ShadowSamplers = prog.ShadowSamplers; + switch (state.option.Fog) { + case OPTION_FOG_EXP: program->FogOption = GL_EXP; break; + case OPTION_FOG_EXP2: program->FogOption = GL_EXP2; break; + case OPTION_FOG_LINEAR: program->FogOption = GL_LINEAR; break; + default: program->FogOption = GL_NONE; break; + } + program->OriginUpperLeft = state.option.OriginUpperLeft; + program->PixelCenterInteger = state.option.PixelCenterInteger; + + program->UsesKill = state.fragment.UsesKill; + + if (program->FogOption) + program->Base.InputsRead |= FRAG_BIT_FOGC; + + if (program->Base.Instructions) + free(program->Base.Instructions); + program->Base.Instructions = prog.Instructions; + + if (program->Base.Parameters) + _mesa_free_parameter_list(program->Base.Parameters); + program->Base.Parameters = prog.Parameters; + + /* Append fog instructions now if the program has "OPTION ARB_fog_exp" + * or similar. We used to leave this up to drivers, but it appears + * there's no hardware that wants to do fog in a discrete stage separate + * from the fragment shader. + */ + if (program->FogOption != GL_NONE) { + /* XXX: we should somehow recompile this to remove clamping if disabled + * On the ATI driver, this is unclampled if fragment clamping is disabled + */ + _mesa_append_fog_code(ctx, program, GL_TRUE); + program->FogOption = GL_NONE; + } + +#if DEBUG_FP + printf("____________Fragment program %u ________\n", program->Base.Id); + _mesa_print_program(&program->Base); +#endif +} + + + +/** + * Parse the vertex program string. If success, update the given + * vertex_program object with the new program. Else, leave the vertex_program + * object unchanged. + */ +void +_mesa_parse_arb_vertex_program(struct gl_context *ctx, GLenum target, + const GLvoid *str, GLsizei len, + struct gl_vertex_program *program) +{ + struct gl_program prog; + struct asm_parser_state state; + + ASSERT(target == GL_VERTEX_PROGRAM_ARB); + + memset(&prog, 0, sizeof(prog)); + memset(&state, 0, sizeof(state)); + state.prog = &prog; + + if (!_mesa_parse_arb_program(ctx, target, (const GLubyte*) str, len, + &state)) { + _mesa_error(ctx, GL_INVALID_OPERATION, "glProgramString(bad program)"); + return; + } + + if (program->Base.String != NULL) + free(program->Base.String); + + /* Copy the relevant contents of the arb_program struct into the + * vertex_program struct. + */ + program->Base.String = prog.String; + program->Base.NumInstructions = prog.NumInstructions; + program->Base.NumTemporaries = prog.NumTemporaries; + program->Base.NumParameters = prog.NumParameters; + program->Base.NumAttributes = prog.NumAttributes; + program->Base.NumAddressRegs = prog.NumAddressRegs; + program->Base.NumNativeInstructions = prog.NumNativeInstructions; + program->Base.NumNativeTemporaries = prog.NumNativeTemporaries; + program->Base.NumNativeParameters = prog.NumNativeParameters; + program->Base.NumNativeAttributes = prog.NumNativeAttributes; + program->Base.NumNativeAddressRegs = prog.NumNativeAddressRegs; + program->Base.InputsRead = prog.InputsRead; + program->Base.OutputsWritten = prog.OutputsWritten; + program->Base.IndirectRegisterFiles = prog.IndirectRegisterFiles; + program->IsPositionInvariant = (state.option.PositionInvariant) + ? GL_TRUE : GL_FALSE; + + if (program->Base.Instructions) + free(program->Base.Instructions); + program->Base.Instructions = prog.Instructions; + + if (program->Base.Parameters) + _mesa_free_parameter_list(program->Base.Parameters); + program->Base.Parameters = prog.Parameters; + +#if DEBUG_VP + printf("____________Vertex program %u __________\n", program->Base.Id); + _mesa_print_program(&program->Base); +#endif +} diff --git a/mesalib/src/mesa/program/ir_to_mesa.cpp b/mesalib/src/mesa/program/ir_to_mesa.cpp index c1b28ec3f..bf2513d47 100644 --- a/mesalib/src/mesa/program/ir_to_mesa.cpp +++ b/mesalib/src/mesa/program/ir_to_mesa.cpp @@ -1,3332 +1,3309 @@ -/* - * Copyright (C) 2005-2007 Brian Paul All Rights Reserved. - * Copyright (C) 2008 VMware, Inc. All Rights Reserved. - * 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_to_mesa.cpp - * - * Translate GLSL IR to Mesa's gl_program representation. - */ - -#include -#include "main/compiler.h" -#include "ir.h" -#include "ir_visitor.h" -#include "ir_print_visitor.h" -#include "ir_expression_flattening.h" -#include "glsl_types.h" -#include "glsl_parser_extras.h" -#include "../glsl/program.h" -#include "ir_optimization.h" -#include "ast.h" - -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); - -/** - * This struct is a corresponding struct to Mesa prog_src_register, with - * wider fields. - */ -typedef struct ir_to_mesa_src_reg { - ir_to_mesa_src_reg(int file, int index, const glsl_type *type) - { - this->file = (gl_register_file) file; - this->index = index; - if (type && (type->is_scalar() || type->is_vector() || type->is_matrix())) - this->swizzle = swizzle_for_size(type->vector_elements); - else - this->swizzle = SWIZZLE_XYZW; - this->negate = 0; - this->reladdr = NULL; - } - - ir_to_mesa_src_reg() - { - this->file = PROGRAM_UNDEFINED; - this->index = 0; - this->swizzle = 0; - this->negate = 0; - this->reladdr = NULL; - } - - gl_register_file file; /**< PROGRAM_* from Mesa */ - int index; /**< temporary index, VERT_ATTRIB_*, FRAG_ATTRIB_*, etc. */ - GLuint swizzle; /**< SWIZZLE_XYZWONEZERO swizzles from Mesa. */ - int negate; /**< NEGATE_XYZW mask from mesa */ - /** Register index should be offset by the integer in this reg. */ - ir_to_mesa_src_reg *reladdr; -} ir_to_mesa_src_reg; - -typedef struct ir_to_mesa_dst_reg { - int file; /**< PROGRAM_* from Mesa */ - int index; /**< temporary index, VERT_ATTRIB_*, FRAG_ATTRIB_*, etc. */ - int writemask; /**< Bitfield of WRITEMASK_[XYZW] */ - GLuint cond_mask:4; - /** Register index should be offset by the integer in this reg. */ - ir_to_mesa_src_reg *reladdr; -} ir_to_mesa_dst_reg; - -extern ir_to_mesa_src_reg ir_to_mesa_undef; - -class ir_to_mesa_instruction : public exec_node { -public: - /* Callers of this ralloc-based new need not call delete. It's - * easier to just ralloc_free 'ctx' (or any of its ancestors). */ - static void* operator new(size_t size, void *ctx) - { - void *node; - - node = rzalloc_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]; - /** Pointer to the ir source this tree came from for debugging */ - ir_instruction *ir; - GLboolean cond_update; - bool saturate; - int sampler; /**< sampler index */ - int tex_target; /**< One of TEXTURE_*_INDEX */ - GLboolean tex_shadow; - - class function_entry *function; /* Set on OPCODE_CAL or OPCODE_BGNSUB */ -}; - -class variable_storage : public exec_node { -public: - variable_storage(ir_variable *var, gl_register_file file, int index) - : file(file), index(index), var(var) - { - /* empty */ - } - - gl_register_file file; - int index; - ir_variable *var; /* variable that maps to this, if any */ -}; - -class function_entry : public exec_node { -public: - ir_function_signature *sig; - - /** - * identifier of this function signature used by the program. - * - * At the point that Mesa instructions for function calls are - * generated, we don't know the address of the first instruction of - * the function body. So we make the BranchTarget that is called a - * small integer and rewrite them during set_branchtargets(). - */ - int sig_id; - - /** - * Pointer to first instruction of the function body. - * - * Set during function body emits after main() is processed. - */ - ir_to_mesa_instruction *bgn_inst; - - /** - * Index of the first instruction of the function body in actual - * Mesa IR. - * - * Set after convertion from ir_to_mesa_instruction to prog_instruction. - */ - int inst; - - /** Storage for the return value. */ - ir_to_mesa_src_reg return_reg; -}; - -class ir_to_mesa_visitor : public ir_visitor { -public: - ir_to_mesa_visitor(); - ~ir_to_mesa_visitor(); - - function_entry *current_function; - - struct gl_context *ctx; - struct gl_program *prog; - struct gl_shader_program *shader_program; - struct gl_shader_compiler_options *options; - - int next_temp; - - variable_storage *find_variable_storage(ir_variable *var); - - function_entry *get_function_signature(ir_function_signature *sig); - - ir_to_mesa_src_reg get_temp(const glsl_type *type); - void reladdr_to_temp(ir_instruction *ir, - ir_to_mesa_src_reg *reg, int *num_reladdr); - - struct ir_to_mesa_src_reg src_reg_for_float(float val); - - /** - * \name Visit methods - * - * As typical for the visitor pattern, there must be one \c visit method for - * each concrete subclass of \c ir_instruction. Virtual base classes within - * the hierarchy should not have \c visit methods. - */ - /*@{*/ - virtual void visit(ir_variable *); - virtual void visit(ir_loop *); - virtual void visit(ir_loop_jump *); - virtual void visit(ir_function_signature *); - virtual void visit(ir_function *); - virtual void visit(ir_expression *); - virtual void visit(ir_swizzle *); - virtual void visit(ir_dereference_variable *); - virtual void visit(ir_dereference_array *); - virtual void visit(ir_dereference_record *); - virtual void visit(ir_assignment *); - virtual void visit(ir_constant *); - virtual void visit(ir_call *); - virtual void visit(ir_return *); - virtual void visit(ir_discard *); - virtual void visit(ir_texture *); - virtual void visit(ir_if *); - /*@}*/ - - struct ir_to_mesa_src_reg result; - - /** List of variable_storage */ - exec_list variables; - - /** List of function_entry */ - exec_list function_signatures; - int next_signature_id; - - /** List of ir_to_mesa_instruction */ - exec_list instructions; - - ir_to_mesa_instruction *ir_to_mesa_emit_op0(ir_instruction *ir, - enum prog_opcode op); - - ir_to_mesa_instruction *ir_to_mesa_emit_op1(ir_instruction *ir, - enum prog_opcode op, - ir_to_mesa_dst_reg dst, - ir_to_mesa_src_reg src0); - - ir_to_mesa_instruction *ir_to_mesa_emit_op2(ir_instruction *ir, - enum prog_opcode op, - ir_to_mesa_dst_reg dst, - ir_to_mesa_src_reg src0, - ir_to_mesa_src_reg src1); - - ir_to_mesa_instruction *ir_to_mesa_emit_op3(ir_instruction *ir, - enum prog_opcode op, - ir_to_mesa_dst_reg dst, - ir_to_mesa_src_reg src0, - ir_to_mesa_src_reg src1, - ir_to_mesa_src_reg src2); - - /** - * Emit the correct dot-product instruction for the type of arguments - * - * \sa ir_to_mesa_emit_op2 - */ - void ir_to_mesa_emit_dp(ir_instruction *ir, - ir_to_mesa_dst_reg dst, - ir_to_mesa_src_reg src0, - ir_to_mesa_src_reg src1, - unsigned elements); - - void ir_to_mesa_emit_scalar_op1(ir_instruction *ir, - enum prog_opcode op, - ir_to_mesa_dst_reg dst, - ir_to_mesa_src_reg src0); - - void ir_to_mesa_emit_scalar_op2(ir_instruction *ir, - enum prog_opcode op, - ir_to_mesa_dst_reg dst, - ir_to_mesa_src_reg src0, - ir_to_mesa_src_reg src1); - - void emit_scs(ir_instruction *ir, enum prog_opcode op, - ir_to_mesa_dst_reg dst, - const ir_to_mesa_src_reg &src); - - GLboolean try_emit_mad(ir_expression *ir, - int mul_operand); - GLboolean try_emit_sat(ir_expression *ir); - - void emit_swz(ir_expression *ir); - - bool process_move_condition(ir_rvalue *ir); - - void copy_propagate(void); - - void *mem_ctx; -}; - -ir_to_mesa_src_reg ir_to_mesa_undef = ir_to_mesa_src_reg(PROGRAM_UNDEFINED, 0, NULL); - -ir_to_mesa_dst_reg ir_to_mesa_undef_dst = { - PROGRAM_UNDEFINED, 0, SWIZZLE_NOOP, COND_TR, NULL, -}; - -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); - ralloc_vasprintf_append(&prog->InfoLog, fmt, args); - va_end(args); - - prog->LinkStatus = GL_FALSE; -} - -static int -swizzle_for_size(int size) -{ - int size_swizzles[4] = { - MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_X, SWIZZLE_X, SWIZZLE_X), - MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Y, SWIZZLE_Y), - MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_Z), - MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_W), - }; - - assert((size >= 1) && (size <= 4)); - return size_swizzles[size - 1]; -} - -ir_to_mesa_instruction * -ir_to_mesa_visitor::ir_to_mesa_emit_op3(ir_instruction *ir, - enum prog_opcode op, - ir_to_mesa_dst_reg dst, - ir_to_mesa_src_reg src0, - ir_to_mesa_src_reg src1, - ir_to_mesa_src_reg src2) -{ - ir_to_mesa_instruction *inst = new(mem_ctx) ir_to_mesa_instruction(); - int num_reladdr = 0; - - /* If we have to do relative addressing, we want to load the ARL - * reg directly for one of the regs, and preload the other reladdr - * sources into temps. - */ - num_reladdr += dst.reladdr != NULL; - num_reladdr += src0.reladdr != NULL; - num_reladdr += src1.reladdr != NULL; - num_reladdr += src2.reladdr != NULL; - - reladdr_to_temp(ir, &src2, &num_reladdr); - reladdr_to_temp(ir, &src1, &num_reladdr); - reladdr_to_temp(ir, &src0, &num_reladdr); - - if (dst.reladdr) { - ir_to_mesa_emit_op1(ir, OPCODE_ARL, ir_to_mesa_address_reg, - *dst.reladdr); - - num_reladdr--; - } - assert(num_reladdr == 0); - - inst->op = op; - inst->dst_reg = dst; - inst->src_reg[0] = src0; - inst->src_reg[1] = src1; - inst->src_reg[2] = src2; - inst->ir = ir; - - inst->function = NULL; - - this->instructions.push_tail(inst); - - return inst; -} - - -ir_to_mesa_instruction * -ir_to_mesa_visitor::ir_to_mesa_emit_op2(ir_instruction *ir, - enum prog_opcode op, - ir_to_mesa_dst_reg dst, - ir_to_mesa_src_reg src0, - ir_to_mesa_src_reg src1) -{ - return ir_to_mesa_emit_op3(ir, op, dst, src0, src1, ir_to_mesa_undef); -} - -ir_to_mesa_instruction * -ir_to_mesa_visitor::ir_to_mesa_emit_op1(ir_instruction *ir, - enum prog_opcode op, - 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); -} - -ir_to_mesa_instruction * -ir_to_mesa_visitor::ir_to_mesa_emit_op0(ir_instruction *ir, - enum prog_opcode op) -{ - return ir_to_mesa_emit_op3(ir, op, ir_to_mesa_undef_dst, - ir_to_mesa_undef, - ir_to_mesa_undef, - ir_to_mesa_undef); -} - -void -ir_to_mesa_visitor::ir_to_mesa_emit_dp(ir_instruction *ir, - ir_to_mesa_dst_reg dst, - ir_to_mesa_src_reg src0, - ir_to_mesa_src_reg src1, - unsigned elements) -{ - static const gl_inst_opcode dot_opcodes[] = { - OPCODE_DP2, OPCODE_DP3, OPCODE_DP4 - }; - - ir_to_mesa_emit_op3(ir, dot_opcodes[elements - 2], - dst, src0, src1, ir_to_mesa_undef); -} - -inline ir_to_mesa_dst_reg -ir_to_mesa_dst_reg_from_src(ir_to_mesa_src_reg reg) -{ - ir_to_mesa_dst_reg dst_reg; - - dst_reg.file = reg.file; - dst_reg.index = reg.index; - dst_reg.writemask = WRITEMASK_XYZW; - dst_reg.cond_mask = COND_TR; - dst_reg.reladdr = reg.reladdr; - - return dst_reg; -} - -inline ir_to_mesa_src_reg -ir_to_mesa_src_reg_from_dst(ir_to_mesa_dst_reg reg) -{ - return ir_to_mesa_src_reg(reg.file, reg.index, NULL); -} - -/** - * Emits Mesa scalar opcodes to produce unique answers across channels. - * - * Some Mesa opcodes are scalar-only, like ARB_fp/vp. The src X - * channel determines the result across all channels. So to do a vec4 - * of this operation, we want to emit a scalar per source channel used - * to produce dest channels. - */ -void -ir_to_mesa_visitor::ir_to_mesa_emit_scalar_op2(ir_instruction *ir, - enum prog_opcode op, - ir_to_mesa_dst_reg dst, - ir_to_mesa_src_reg orig_src0, - ir_to_mesa_src_reg orig_src1) -{ - int i, j; - int done_mask = ~dst.writemask; - - /* Mesa RCP is a scalar operation splatting results to all channels, - * like ARB_fp/vp. So emit as many RCPs as necessary to cover our - * dst channels. - */ - for (i = 0; i < 4; i++) { - GLuint this_mask = (1 << i); - ir_to_mesa_instruction *inst; - ir_to_mesa_src_reg src0 = orig_src0; - ir_to_mesa_src_reg src1 = orig_src1; - - if (done_mask & this_mask) - continue; - - GLuint src0_swiz = GET_SWZ(src0.swizzle, i); - GLuint src1_swiz = GET_SWZ(src1.swizzle, i); - for (j = i + 1; j < 4; j++) { - /* If there is another enabled component in the destination that is - * derived from the same inputs, generate its value on this pass as - * well. - */ - if (!(done_mask & (1 << j)) && - GET_SWZ(src0.swizzle, j) == src0_swiz && - GET_SWZ(src1.swizzle, j) == src1_swiz) { - this_mask |= (1 << j); - } - } - src0.swizzle = MAKE_SWIZZLE4(src0_swiz, src0_swiz, - src0_swiz, src0_swiz); - src1.swizzle = MAKE_SWIZZLE4(src1_swiz, src1_swiz, - src1_swiz, src1_swiz); - - inst = ir_to_mesa_emit_op2(ir, op, - dst, - src0, - src1); - inst->dst_reg.writemask = this_mask; - done_mask |= this_mask; - } -} - -void -ir_to_mesa_visitor::ir_to_mesa_emit_scalar_op1(ir_instruction *ir, - enum prog_opcode op, - ir_to_mesa_dst_reg dst, - ir_to_mesa_src_reg src0) -{ - ir_to_mesa_src_reg undef = ir_to_mesa_undef; - - undef.swizzle = SWIZZLE_XXXX; - - ir_to_mesa_emit_scalar_op2(ir, op, dst, src0, undef); -} - -/** - * Emit an OPCODE_SCS instruction - * - * The \c SCS opcode functions a bit differently than the other Mesa (or - * ARB_fragment_program) opcodes. Instead of splatting its result across all - * four components of the destination, it writes one value to the \c x - * component and another value to the \c y component. - * - * \param ir IR instruction being processed - * \param op Either \c OPCODE_SIN or \c OPCODE_COS depending on which - * value is desired. - * \param dst Destination register - * \param src Source register - */ -void -ir_to_mesa_visitor::emit_scs(ir_instruction *ir, enum prog_opcode op, - ir_to_mesa_dst_reg dst, - const ir_to_mesa_src_reg &src) -{ - /* Vertex programs cannot use the SCS opcode. - */ - if (this->prog->Target == GL_VERTEX_PROGRAM_ARB) { - ir_to_mesa_emit_scalar_op1(ir, op, dst, src); - return; - } - - const unsigned component = (op == OPCODE_SIN) ? 0 : 1; - const unsigned scs_mask = (1U << component); - int done_mask = ~dst.writemask; - ir_to_mesa_src_reg tmp; - - assert(op == OPCODE_SIN || op == OPCODE_COS); - - /* If there are compnents in the destination that differ from the component - * that will be written by the SCS instrution, we'll need a temporary. - */ - if (scs_mask != unsigned(dst.writemask)) { - tmp = get_temp(glsl_type::vec4_type); - } - - for (unsigned i = 0; i < 4; i++) { - unsigned this_mask = (1U << i); - ir_to_mesa_src_reg src0 = src; - - if ((done_mask & this_mask) != 0) - continue; - - /* The source swizzle specified which component of the source generates - * sine / cosine for the current component in the destination. The SCS - * instruction requires that this value be swizzle to the X component. - * Replace the current swizzle with a swizzle that puts the source in - * the X component. - */ - unsigned src0_swiz = GET_SWZ(src.swizzle, i); - - src0.swizzle = MAKE_SWIZZLE4(src0_swiz, src0_swiz, - src0_swiz, src0_swiz); - for (unsigned j = i + 1; j < 4; j++) { - /* If there is another enabled component in the destination that is - * derived from the same inputs, generate its value on this pass as - * well. - */ - if (!(done_mask & (1 << j)) && - GET_SWZ(src0.swizzle, j) == src0_swiz) { - this_mask |= (1 << j); - } - } - - if (this_mask != scs_mask) { - ir_to_mesa_instruction *inst; - ir_to_mesa_dst_reg tmp_dst = ir_to_mesa_dst_reg_from_src(tmp); - - /* Emit the SCS instruction. - */ - inst = ir_to_mesa_emit_op1(ir, OPCODE_SCS, tmp_dst, src0); - inst->dst_reg.writemask = scs_mask; - - /* Move the result of the SCS instruction to the desired location in - * the destination. - */ - tmp.swizzle = MAKE_SWIZZLE4(component, component, - component, component); - inst = ir_to_mesa_emit_op1(ir, OPCODE_SCS, dst, tmp); - inst->dst_reg.writemask = this_mask; - } else { - /* Emit the SCS instruction to write directly to the destination. - */ - ir_to_mesa_instruction *inst = - ir_to_mesa_emit_op1(ir, OPCODE_SCS, dst, src0); - inst->dst_reg.writemask = scs_mask; - } - - done_mask |= this_mask; - } -} - -struct ir_to_mesa_src_reg -ir_to_mesa_visitor::src_reg_for_float(float val) -{ - ir_to_mesa_src_reg src_reg(PROGRAM_CONSTANT, -1, NULL); - - src_reg.index = _mesa_add_unnamed_constant(this->prog->Parameters, - &val, 1, &src_reg.swizzle); - - return src_reg; -} - -static int -type_size(const struct glsl_type *type) -{ - unsigned int i; - int size; - - switch (type->base_type) { - case GLSL_TYPE_UINT: - case GLSL_TYPE_INT: - case GLSL_TYPE_FLOAT: - case GLSL_TYPE_BOOL: - if (type->is_matrix()) { - return type->matrix_columns; - } else { - /* Regardless of size of vector, it gets a vec4. This is bad - * packing for things like floats, but otherwise arrays become a - * mess. Hopefully a later pass over the code can pack scalars - * down if appropriate. - */ - return 1; - } - case GLSL_TYPE_ARRAY: - assert(type->length > 0); - return type_size(type->fields.array) * type->length; - case GLSL_TYPE_STRUCT: - size = 0; - for (i = 0; i < type->length; i++) { - 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; - } -} - -/** - * In the initial pass of codegen, we assign temporary numbers to - * intermediate results. (not SSA -- variable assignments will reuse - * storage). Actual register allocation for the Mesa VM occurs in a - * pass over the Mesa IR later. - */ -ir_to_mesa_src_reg -ir_to_mesa_visitor::get_temp(const glsl_type *type) -{ - ir_to_mesa_src_reg src_reg; - int swizzle[4]; - int i; - - src_reg.file = PROGRAM_TEMPORARY; - src_reg.index = next_temp; - src_reg.reladdr = NULL; - next_temp += type_size(type); - - if (type->is_array() || type->is_record()) { - src_reg.swizzle = SWIZZLE_NOOP; - } else { - for (i = 0; i < type->vector_elements; i++) - swizzle[i] = i; - for (; i < 4; i++) - swizzle[i] = type->vector_elements - 1; - src_reg.swizzle = MAKE_SWIZZLE4(swizzle[0], swizzle[1], - swizzle[2], swizzle[3]); - } - src_reg.negate = 0; - - return src_reg; -} - -variable_storage * -ir_to_mesa_visitor::find_variable_storage(ir_variable *var) -{ - - variable_storage *entry; - - foreach_iter(exec_list_iterator, iter, this->variables) { - entry = (variable_storage *)iter.get(); - - if (entry->var == var) - return entry; - } - - return NULL; -} - -void -ir_to_mesa_visitor::visit(ir_variable *ir) -{ - if (strcmp(ir->name, "gl_FragCoord") == 0) { - struct gl_fragment_program *fp = (struct gl_fragment_program *)this->prog; - - fp->OriginUpperLeft = ir->origin_upper_left; - fp->PixelCenterInteger = ir->pixel_center_integer; - - } else if (strcmp(ir->name, "gl_FragDepth") == 0) { - struct gl_fragment_program *fp = (struct gl_fragment_program *)this->prog; - switch (ir->depth_layout) { - case ir_depth_layout_none: - fp->FragDepthLayout = FRAG_DEPTH_LAYOUT_NONE; - break; - case ir_depth_layout_any: - fp->FragDepthLayout = FRAG_DEPTH_LAYOUT_ANY; - break; - case ir_depth_layout_greater: - fp->FragDepthLayout = FRAG_DEPTH_LAYOUT_GREATER; - break; - case ir_depth_layout_less: - fp->FragDepthLayout = FRAG_DEPTH_LAYOUT_LESS; - break; - case ir_depth_layout_unchanged: - fp->FragDepthLayout = FRAG_DEPTH_LAYOUT_UNCHANGED; - break; - default: - assert(0); - break; - } - } - - 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) -{ - 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); -} - -void -ir_to_mesa_visitor::visit(ir_loop_jump *ir) -{ - switch (ir->mode) { - case ir_loop_jump::jump_break: - ir_to_mesa_emit_op0(NULL, OPCODE_BRK); - break; - case ir_loop_jump::jump_continue: - ir_to_mesa_emit_op0(NULL, OPCODE_CONT); - break; - } -} - - -void -ir_to_mesa_visitor::visit(ir_function_signature *ir) -{ - assert(0); - (void)ir; -} - -void -ir_to_mesa_visitor::visit(ir_function *ir) -{ - /* Ignore function bodies other than main() -- we shouldn't see calls to - * them since they should all be inlined before we get to ir_to_mesa. - */ - if (strcmp(ir->name, "main") == 0) { - const ir_function_signature *sig; - exec_list empty; - - sig = ir->matching_signature(&empty); - - assert(sig); - - foreach_iter(exec_list_iterator, iter, sig->body) { - ir_instruction *ir = (ir_instruction *)iter.get(); - - ir->accept(this); - } - } -} - -GLboolean -ir_to_mesa_visitor::try_emit_mad(ir_expression *ir, int mul_operand) -{ - int nonmul_operand = 1 - mul_operand; - ir_to_mesa_src_reg a, b, c; - - ir_expression *expr = ir->operands[mul_operand]->as_expression(); - if (!expr || expr->operation != ir_binop_mul) - return false; - - expr->operands[0]->accept(this); - a = this->result; - expr->operands[1]->accept(this); - b = this->result; - ir->operands[nonmul_operand]->accept(this); - c = this->result; - - this->result = get_temp(ir->type); - ir_to_mesa_emit_op3(ir, OPCODE_MAD, - ir_to_mesa_dst_reg_from_src(this->result), a, b, c); - - return true; -} - -GLboolean -ir_to_mesa_visitor::try_emit_sat(ir_expression *ir) -{ - /* Saturates were only introduced to vertex programs in - * NV_vertex_program3, so don't give them to drivers in the VP. - */ - if (this->prog->Target == GL_VERTEX_PROGRAM_ARB) - return false; - - ir_rvalue *sat_src = ir->as_rvalue_to_saturate(); - if (!sat_src) - return false; - - sat_src->accept(this); - ir_to_mesa_src_reg src = this->result; - - this->result = get_temp(ir->type); - ir_to_mesa_instruction *inst; - inst = ir_to_mesa_emit_op1(ir, OPCODE_MOV, - ir_to_mesa_dst_reg_from_src(this->result), - src); - inst->saturate = true; - - return true; -} - -void -ir_to_mesa_visitor::reladdr_to_temp(ir_instruction *ir, - ir_to_mesa_src_reg *reg, int *num_reladdr) -{ - if (!reg->reladdr) - return; - - ir_to_mesa_emit_op1(ir, OPCODE_ARL, ir_to_mesa_address_reg, *reg->reladdr); - - if (*num_reladdr != 1) { - ir_to_mesa_src_reg temp = get_temp(glsl_type::vec4_type); - - ir_to_mesa_emit_op1(ir, OPCODE_MOV, - ir_to_mesa_dst_reg_from_src(temp), *reg); - *reg = temp; - } - - (*num_reladdr)--; -} - -void -ir_to_mesa_visitor::emit_swz(ir_expression *ir) -{ - /* Assume that the vector operator is in a form compatible with OPCODE_SWZ. - * This means that each of the operands is either an immediate value of -1, - * 0, or 1, or is a component from one source register (possibly with - * negation). - */ - uint8_t components[4] = { 0 }; - bool negate[4] = { false }; - ir_variable *var = NULL; - - for (unsigned i = 0; i < ir->type->vector_elements; i++) { - ir_rvalue *op = ir->operands[i]; - - assert(op->type->is_scalar()); - - while (op != NULL) { - switch (op->ir_type) { - case ir_type_constant: { - - assert(op->type->is_scalar()); - - const ir_constant *const c = op->as_constant(); - if (c->is_one()) { - components[i] = SWIZZLE_ONE; - } else if (c->is_zero()) { - components[i] = SWIZZLE_ZERO; - } else if (c->is_negative_one()) { - components[i] = SWIZZLE_ONE; - negate[i] = true; - } else { - assert(!"SWZ constant must be 0.0 or 1.0."); - } - - op = NULL; - break; - } - - case ir_type_dereference_variable: { - ir_dereference_variable *const deref = - (ir_dereference_variable *) op; - - assert((var == NULL) || (deref->var == var)); - components[i] = SWIZZLE_X; - var = deref->var; - op = NULL; - break; - } - - case ir_type_expression: { - ir_expression *const expr = (ir_expression *) op; - - assert(expr->operation == ir_unop_neg); - negate[i] = true; - - op = expr->operands[0]; - break; - } - - case ir_type_swizzle: { - ir_swizzle *const swiz = (ir_swizzle *) op; - - components[i] = swiz->mask.x; - op = swiz->val; - break; - } - - default: - assert(!"Should not get here."); - return; - } - } - } - - assert(var != NULL); - - ir_dereference_variable *const deref = - new(mem_ctx) ir_dereference_variable(var); - - this->result.file = PROGRAM_UNDEFINED; - deref->accept(this); - if (this->result.file == PROGRAM_UNDEFINED) { - ir_print_visitor v; - printf("Failed to get tree for expression operand:\n"); - deref->accept(&v); - exit(1); - } - - ir_to_mesa_src_reg src; - - src = this->result; - src.swizzle = MAKE_SWIZZLE4(components[0], - components[1], - components[2], - components[3]); - src.negate = ((unsigned(negate[0]) << 0) - | (unsigned(negate[1]) << 1) - | (unsigned(negate[2]) << 2) - | (unsigned(negate[3]) << 3)); - - /* Storage for our result. Ideally for an assignment we'd be using the - * actual storage for the result here, instead. - */ - const ir_to_mesa_src_reg result_src = get_temp(ir->type); - ir_to_mesa_dst_reg result_dst = ir_to_mesa_dst_reg_from_src(result_src); - - /* Limit writes to the channels that will be used by result_src later. - * This does limit this temp's use as a temporary for multi-instruction - * sequences. - */ - result_dst.writemask = (1 << ir->type->vector_elements) - 1; - - ir_to_mesa_emit_op1(ir, OPCODE_SWZ, result_dst, src); - this->result = result_src; -} - -void -ir_to_mesa_visitor::visit(ir_expression *ir) -{ - unsigned int operand; - struct ir_to_mesa_src_reg op[Elements(ir->operands)]; - struct ir_to_mesa_src_reg result_src; - struct ir_to_mesa_dst_reg result_dst; - - /* Quick peephole: Emit OPCODE_MAD(a, b, c) instead of ADD(MUL(a, b), c) - */ - if (ir->operation == ir_binop_add) { - if (try_emit_mad(ir, 1)) - return; - if (try_emit_mad(ir, 0)) - return; - } - if (try_emit_sat(ir)) - return; - - if (ir->operation == ir_quadop_vector) { - this->emit_swz(ir); - return; - } - - for (operand = 0; operand < ir->get_num_operands(); operand++) { - this->result.file = PROGRAM_UNDEFINED; - ir->operands[operand]->accept(this); - if (this->result.file == PROGRAM_UNDEFINED) { - ir_print_visitor v; - printf("Failed to get tree for expression operand:\n"); - ir->operands[operand]->accept(&v); - exit(1); - } - op[operand] = this->result; - - /* Matrix expression operands should have been broken down to vector - * operations already. - */ - 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 - * the actual storage for the result here, instead. - */ - result_src = get_temp(ir->type); - /* convenience for the emit functions below. */ - result_dst = ir_to_mesa_dst_reg_from_src(result_src); - /* Limit writes to the channels that will be used by result_src later. - * This does limit this temp's use as a temporary for multi-instruction - * sequences. - */ - result_dst.writemask = (1 << ir->type->vector_elements) - 1; - - switch (ir->operation) { - case ir_unop_logic_not: - ir_to_mesa_emit_op2(ir, OPCODE_SEQ, result_dst, - op[0], src_reg_for_float(0.0)); - break; - case ir_unop_neg: - op[0].negate = ~op[0].negate; - result_src = op[0]; - break; - case ir_unop_abs: - ir_to_mesa_emit_op1(ir, OPCODE_ABS, result_dst, op[0]); - break; - case ir_unop_sign: - ir_to_mesa_emit_op1(ir, OPCODE_SSG, result_dst, op[0]); - break; - case ir_unop_rcp: - ir_to_mesa_emit_scalar_op1(ir, OPCODE_RCP, result_dst, 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: - 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]); - break; - case ir_unop_sin: - ir_to_mesa_emit_scalar_op1(ir, OPCODE_SIN, result_dst, op[0]); - break; - case ir_unop_cos: - ir_to_mesa_emit_scalar_op1(ir, OPCODE_COS, result_dst, op[0]); - break; - case ir_unop_sin_reduced: - emit_scs(ir, OPCODE_SIN, result_dst, op[0]); - break; - case ir_unop_cos_reduced: - emit_scs(ir, OPCODE_COS, result_dst, op[0]); - break; - - case ir_unop_dFdx: - ir_to_mesa_emit_op1(ir, OPCODE_DDX, result_dst, op[0]); - break; - case ir_unop_dFdy: - 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; - case ir_binop_sub: - ir_to_mesa_emit_op2(ir, OPCODE_SUB, result_dst, op[0], op[1]); - break; - - case ir_binop_mul: - ir_to_mesa_emit_op2(ir, OPCODE_MUL, result_dst, op[0], op[1]); - break; - case ir_binop_div: - assert(!"not reached: should be handled by ir_div_to_mul_rcp"); - case ir_binop_mod: - assert(!"ir_binop_mod should have been converted to b * fract(a/b)"); - break; - - case ir_binop_less: - ir_to_mesa_emit_op2(ir, OPCODE_SLT, result_dst, op[0], op[1]); - break; - case ir_binop_greater: - ir_to_mesa_emit_op2(ir, OPCODE_SGT, result_dst, op[0], op[1]); - break; - case ir_binop_lequal: - ir_to_mesa_emit_op2(ir, OPCODE_SLE, result_dst, op[0], op[1]); - break; - case ir_binop_gequal: - ir_to_mesa_emit_op2(ir, OPCODE_SGE, result_dst, op[0], op[1]); - break; - case ir_binop_equal: - ir_to_mesa_emit_op2(ir, OPCODE_SEQ, result_dst, op[0], op[1]); - break; - 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]); - ir_to_mesa_emit_dp(ir, result_dst, temp, temp, vector_elements); - 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]); - ir_to_mesa_emit_dp(ir, result_dst, temp, temp, vector_elements); - 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: - assert(ir->operands[0]->type->is_vector()); - ir_to_mesa_emit_dp(ir, result_dst, op[0], op[0], - ir->operands[0]->type->vector_elements); - 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. */ - ir_to_mesa_emit_op2(ir, OPCODE_ADD, - result_dst, - op[0], op[1]); - - ir_to_mesa_emit_op2(ir, OPCODE_SNE, - result_dst, - result_src, src_reg_for_float(0.0)); - break; - - case ir_binop_logic_and: - /* the bool args are stored as float 0.0 or 1.0, so "mul" gives us "and". */ - ir_to_mesa_emit_op2(ir, OPCODE_MUL, - result_dst, - op[0], op[1]); - break; - - case ir_binop_dot: - assert(ir->operands[0]->type->is_vector()); - assert(ir->operands[0]->type == ir->operands[1]->type); - ir_to_mesa_emit_dp(ir, result_dst, op[0], op[1], - ir->operands[0]->type->vector_elements); - 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_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], 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]); - break; - case ir_unop_i2f: - case ir_unop_b2f: - case ir_unop_b2i: - /* Mesa IR lacks types, ints are stored as truncated floats. */ - result_src = op[0]; - break; - case ir_unop_f2i: - ir_to_mesa_emit_op1(ir, OPCODE_TRUNC, result_dst, op[0]); - break; - case ir_unop_f2b: - case ir_unop_i2b: - ir_to_mesa_emit_op2(ir, OPCODE_SNE, result_dst, - 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]); - break; - case ir_unop_ceil: - op[0].negate = ~op[0].negate; - ir_to_mesa_emit_op1(ir, OPCODE_FLR, result_dst, op[0]); - result_src.negate = ~result_src.negate; - break; - case ir_unop_floor: - ir_to_mesa_emit_op1(ir, OPCODE_FLR, result_dst, op[0]); - break; - case ir_unop_fract: - ir_to_mesa_emit_op1(ir, OPCODE_FRC, result_dst, op[0]); - break; - - case ir_binop_min: - ir_to_mesa_emit_op2(ir, OPCODE_MIN, result_dst, op[0], op[1]); - break; - case ir_binop_max: - ir_to_mesa_emit_op2(ir, OPCODE_MAX, result_dst, op[0], op[1]); - break; - case ir_binop_pow: - ir_to_mesa_emit_scalar_op2(ir, OPCODE_POW, result_dst, op[0], op[1]); - break; - - case ir_unop_bit_not: - case ir_unop_u2f: - case ir_binop_lshift: - case ir_binop_rshift: - case ir_binop_bit_and: - case ir_binop_bit_xor: - case ir_binop_bit_or: - case ir_unop_round_even: - assert(!"GLSL 1.30 features unsupported"); - break; - - case ir_quadop_vector: - /* This operation should have already been handled. - */ - assert(!"Should not get here."); - break; - } - - this->result = result_src; -} - - -void -ir_to_mesa_visitor::visit(ir_swizzle *ir) -{ - ir_to_mesa_src_reg src_reg; - int i; - int swizzle[4]; - - /* Note that this is only swizzles in expressions, not those on the left - * hand side of an assignment, which do write masking. See ir_assignment - * for that. - */ - - ir->val->accept(this); - src_reg = this->result; - assert(src_reg.file != PROGRAM_UNDEFINED); - - for (i = 0; i < 4; i++) { - if (i < ir->type->vector_elements) { - switch (i) { - case 0: - swizzle[i] = GET_SWZ(src_reg.swizzle, ir->mask.x); - break; - case 1: - swizzle[i] = GET_SWZ(src_reg.swizzle, ir->mask.y); - break; - case 2: - swizzle[i] = GET_SWZ(src_reg.swizzle, ir->mask.z); - break; - case 3: - swizzle[i] = GET_SWZ(src_reg.swizzle, ir->mask.w); - break; - } - } else { - /* If the type is smaller than a vec4, replicate the last - * channel out. - */ - swizzle[i] = swizzle[ir->type->vector_elements - 1]; - } - } - - src_reg.swizzle = MAKE_SWIZZLE4(swizzle[0], - swizzle[1], - swizzle[2], - swizzle[3]); - - this->result = src_reg; -} - -void -ir_to_mesa_visitor::visit(ir_dereference_variable *ir) -{ - variable_storage *entry = find_variable_storage(ir->var); - ir_variable *var = ir->var; - - if (!entry) { - switch (var->mode) { - case ir_var_uniform: - entry = new(mem_ctx) variable_storage(var, PROGRAM_UNIFORM, - var->location); - this->variables.push_tail(entry); - break; - case ir_var_in: - case ir_var_inout: - /* The linker assigns locations for varyings and attributes, - * including deprecated builtins (like gl_Color), user-assign - * generic attributes (glBindVertexLocation), and - * user-defined varyings. - * - * FINISHME: We would hit this path for function arguments. Fix! - */ - assert(var->location != -1); - entry = new(mem_ctx) variable_storage(var, - PROGRAM_INPUT, - var->location); - if (this->prog->Target == GL_VERTEX_PROGRAM_ARB && - var->location >= VERT_ATTRIB_GENERIC0) { - _mesa_add_attribute(this->prog->Attributes, - var->name, - _mesa_sizeof_glsl_type(var->type->gl_type), - var->type->gl_type, - var->location - VERT_ATTRIB_GENERIC0); - } - break; - case ir_var_out: - assert(var->location != -1); - entry = new(mem_ctx) variable_storage(var, - PROGRAM_OUTPUT, - var->location); - break; - case ir_var_system_value: - entry = new(mem_ctx) variable_storage(var, - PROGRAM_SYSTEM_VALUE, - var->location); - break; - case ir_var_auto: - case ir_var_temporary: - entry = new(mem_ctx) variable_storage(var, PROGRAM_TEMPORARY, - this->next_temp); - this->variables.push_tail(entry); - - next_temp += type_size(var->type); - break; - } - - if (!entry) { - printf("Failed to make storage for %s\n", var->name); - exit(1); - } - } - - this->result = ir_to_mesa_src_reg(entry->file, entry->index, var->type); -} - -void -ir_to_mesa_visitor::visit(ir_dereference_array *ir) -{ - ir_constant *index; - ir_to_mesa_src_reg src_reg; - int element_size = type_size(ir->type); - - index = ir->array_index->constant_expression_value(); - - ir->array->accept(this); - src_reg = this->result; - - if (index) { - src_reg.index += index->value.i[0] * element_size; - } else { - ir_to_mesa_src_reg array_base = this->result; - /* Variable index array dereference. It eats the "vec4" of the - * base of the array and an index that offsets the Mesa register - * index. - */ - ir->array_index->accept(this); - - ir_to_mesa_src_reg index_reg; - - if (element_size == 1) { - index_reg = this->result; - } else { - index_reg = get_temp(glsl_type::float_type); - - 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 = ralloc(mem_ctx, ir_to_mesa_src_reg); - memcpy(src_reg.reladdr, &index_reg, sizeof(index_reg)); - } - - /* If the type is smaller than a vec4, replicate the last channel out. */ - if (ir->type->is_scalar() || ir->type->is_vector()) - src_reg.swizzle = swizzle_for_size(ir->type->vector_elements); - else - src_reg.swizzle = SWIZZLE_NOOP; - - this->result = src_reg; -} - -void -ir_to_mesa_visitor::visit(ir_dereference_record *ir) -{ - unsigned int i; - const glsl_type *struct_type = ir->record->type; - int offset = 0; - - ir->record->accept(this); - - for (i = 0; i < struct_type->length; i++) { - if (strcmp(struct_type->fields.structure[i].name, ir->field) == 0) - break; - offset += type_size(struct_type->fields.structure[i].type); - } - - /* If the type is smaller than a vec4, replicate the last channel out. */ - if (ir->type->is_scalar() || ir->type->is_vector()) - this->result.swizzle = swizzle_for_size(ir->type->vector_elements); - else - this->result.swizzle = SWIZZLE_NOOP; - - this->result.index += offset; -} - -/** - * 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. - */ -static struct ir_to_mesa_dst_reg -get_assignment_lhs(ir_dereference *ir, ir_to_mesa_visitor *v) -{ - /* 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(); - if (deref_array) { - assert(!deref_array->array->type->is_vector()); - } - - /* Use the rvalue deref handler for the most part. We'll ignore - * swizzles in it and write swizzles using writemask, though. - */ - ir->accept(v); - return ir_to_mesa_dst_reg_from_src(v->result); -} - -/** - * Process the condition of a conditional assignment - * - * Examines the condition of a conditional assignment to generate the optimal - * first operand of a \c CMP instruction. If the condition is a relational - * operator with 0 (e.g., \c ir_binop_less), the value being compared will be - * used as the source for the \c CMP instruction. Otherwise the comparison - * is processed to a boolean result, and the boolean result is used as the - * operand to the CMP instruction. - */ -bool -ir_to_mesa_visitor::process_move_condition(ir_rvalue *ir) -{ - ir_rvalue *src_ir = ir; - bool negate = true; - bool switch_order = false; - - ir_expression *const expr = ir->as_expression(); - if ((expr != NULL) && (expr->get_num_operands() == 2)) { - bool zero_on_left = false; - - if (expr->operands[0]->is_zero()) { - src_ir = expr->operands[1]; - zero_on_left = true; - } else if (expr->operands[1]->is_zero()) { - src_ir = expr->operands[0]; - zero_on_left = false; - } - - /* a is - 0 + - 0 + - * (a < 0) T F F ( a < 0) T F F - * (0 < a) F F T (-a < 0) F F T - * (a <= 0) T T F (-a < 0) F F T (swap order of other operands) - * (0 <= a) F T T ( a < 0) T F F (swap order of other operands) - * (a > 0) F F T (-a < 0) F F T - * (0 > a) T F F ( a < 0) T F F - * (a >= 0) F T T ( a < 0) T F F (swap order of other operands) - * (0 >= a) T T F (-a < 0) F F T (swap order of other operands) - * - * Note that exchanging the order of 0 and 'a' in the comparison simply - * means that the value of 'a' should be negated. - */ - if (src_ir != ir) { - switch (expr->operation) { - case ir_binop_less: - switch_order = false; - negate = zero_on_left; - break; - - case ir_binop_greater: - switch_order = false; - negate = !zero_on_left; - break; - - case ir_binop_lequal: - switch_order = true; - negate = !zero_on_left; - break; - - case ir_binop_gequal: - switch_order = true; - negate = zero_on_left; - break; - - default: - /* This isn't the right kind of comparison afterall, so make sure - * the whole condition is visited. - */ - src_ir = ir; - break; - } - } - } - - src_ir->accept(this); - - /* We use the OPCODE_CMP (a < 0 ? b : c) for conditional moves, and the - * condition we produced is 0.0 or 1.0. By flipping the sign, we can - * choose which value OPCODE_CMP produces without an extra instruction - * computing the condition. - */ - if (negate) - this->result.negate = ~this->result.negate; - - return switch_order; -} - -void -ir_to_mesa_visitor::visit(ir_assignment *ir) -{ - struct ir_to_mesa_dst_reg l; - struct ir_to_mesa_src_reg r; - int i; - - ir->rhs->accept(this); - r = this->result; - - 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); - - if (ir->condition) { - const bool switch_order = this->process_move_condition(ir->condition); - ir_to_mesa_src_reg condition = this->result; - - for (i = 0; i < type_size(ir->lhs->type); i++) { - if (switch_order) { - ir_to_mesa_emit_op3(ir, OPCODE_CMP, l, - condition, ir_to_mesa_src_reg_from_dst(l), r); - } else { - ir_to_mesa_emit_op3(ir, OPCODE_CMP, l, - condition, r, ir_to_mesa_src_reg_from_dst(l)); - } - - l.index++; - r.index++; - } - } else { - for (i = 0; i < type_size(ir->lhs->type); i++) { - ir_to_mesa_emit_op1(ir, OPCODE_MOV, l, r); - l.index++; - r.index++; - } - } -} - - -void -ir_to_mesa_visitor::visit(ir_constant *ir) -{ - ir_to_mesa_src_reg src_reg; - GLfloat stack_vals[4] = { 0 }; - GLfloat *values = stack_vals; - unsigned int i; - - /* Unfortunately, 4 floats is all we can get into - * _mesa_add_unnamed_constant. So, make a temp to store an - * aggregate constant and move each constant value into it. If we - * get lucky, copy propagation will eliminate the extra moves. - */ - - if (ir->type->base_type == GLSL_TYPE_STRUCT) { - ir_to_mesa_src_reg temp_base = get_temp(ir->type); - ir_to_mesa_dst_reg temp = ir_to_mesa_dst_reg_from_src(temp_base); - - foreach_iter(exec_list_iterator, iter, ir->components) { - ir_constant *field_value = (ir_constant *)iter.get(); - int size = type_size(field_value->type); - - assert(size > 0); - - field_value->accept(this); - src_reg = this->result; - - for (i = 0; i < (unsigned int)size; i++) { - ir_to_mesa_emit_op1(ir, OPCODE_MOV, temp, src_reg); - - src_reg.index++; - temp.index++; - } - } - this->result = temp_base; - return; - } - - if (ir->type->is_array()) { - ir_to_mesa_src_reg temp_base = get_temp(ir->type); - ir_to_mesa_dst_reg temp = ir_to_mesa_dst_reg_from_src(temp_base); - int size = type_size(ir->type->fields.array); - - assert(size > 0); - - for (i = 0; i < ir->type->length; i++) { - ir->array_elements[i]->accept(this); - src_reg = this->result; - for (int j = 0; j < size; j++) { - ir_to_mesa_emit_op1(ir, OPCODE_MOV, temp, src_reg); - - src_reg.index++; - temp.index++; - } - } - this->result = temp_base; - return; - } - - if (ir->type->is_matrix()) { - ir_to_mesa_src_reg mat = get_temp(ir->type); - ir_to_mesa_dst_reg mat_column = ir_to_mesa_dst_reg_from_src(mat); - - for (i = 0; i < ir->type->matrix_columns; i++) { - assert(ir->type->base_type == GLSL_TYPE_FLOAT); - values = &ir->value.f[i * ir->type->vector_elements]; - - src_reg = ir_to_mesa_src_reg(PROGRAM_CONSTANT, -1, NULL); - src_reg.index = _mesa_add_unnamed_constant(this->prog->Parameters, - values, - ir->type->vector_elements, - &src_reg.swizzle); - ir_to_mesa_emit_op1(ir, OPCODE_MOV, mat_column, src_reg); - - mat_column.index++; - } - - this->result = mat; - return; - } - - src_reg.file = PROGRAM_CONSTANT; - switch (ir->type->base_type) { - case GLSL_TYPE_FLOAT: - values = &ir->value.f[0]; - break; - case GLSL_TYPE_UINT: - for (i = 0; i < ir->type->vector_elements; i++) { - values[i] = ir->value.u[i]; - } - break; - case GLSL_TYPE_INT: - for (i = 0; i < ir->type->vector_elements; i++) { - values[i] = ir->value.i[i]; - } - break; - case GLSL_TYPE_BOOL: - for (i = 0; i < ir->type->vector_elements; i++) { - values[i] = ir->value.b[i]; - } - break; - default: - assert(!"Non-float/uint/int/bool constant"); - } - - this->result = ir_to_mesa_src_reg(PROGRAM_CONSTANT, -1, ir->type); - this->result.index = _mesa_add_unnamed_constant(this->prog->Parameters, - values, - ir->type->vector_elements, - &this->result.swizzle); -} - -function_entry * -ir_to_mesa_visitor::get_function_signature(ir_function_signature *sig) -{ - function_entry *entry; - - foreach_iter(exec_list_iterator, iter, this->function_signatures) { - entry = (function_entry *)iter.get(); - - if (entry->sig == sig) - return entry; - } - - entry = ralloc(mem_ctx, function_entry); - entry->sig = sig; - entry->sig_id = this->next_signature_id++; - entry->bgn_inst = NULL; - - /* Allocate storage for all the parameters. */ - foreach_iter(exec_list_iterator, iter, sig->parameters) { - ir_variable *param = (ir_variable *)iter.get(); - variable_storage *storage; - - storage = find_variable_storage(param); - assert(!storage); - - storage = new(mem_ctx) variable_storage(param, PROGRAM_TEMPORARY, - this->next_temp); - this->variables.push_tail(storage); - - this->next_temp += type_size(param->type); - } - - if (!sig->return_type->is_void()) { - entry->return_reg = get_temp(sig->return_type); - } else { - entry->return_reg = ir_to_mesa_undef; - } - - this->function_signatures.push_tail(entry); - return entry; -} - -void -ir_to_mesa_visitor::visit(ir_call *ir) -{ - ir_to_mesa_instruction *call_inst; - ir_function_signature *sig = ir->get_callee(); - function_entry *entry = get_function_signature(sig); - int i; - - /* Process in parameters. */ - exec_list_iterator sig_iter = sig->parameters.iterator(); - foreach_iter(exec_list_iterator, iter, *ir) { - ir_rvalue *param_rval = (ir_rvalue *)iter.get(); - ir_variable *param = (ir_variable *)sig_iter.get(); - - if (param->mode == ir_var_in || - param->mode == ir_var_inout) { - variable_storage *storage = find_variable_storage(param); - assert(storage); - - param_rval->accept(this); - ir_to_mesa_src_reg r = this->result; - - ir_to_mesa_dst_reg l; - l.file = storage->file; - l.index = storage->index; - l.reladdr = NULL; - l.writemask = WRITEMASK_XYZW; - l.cond_mask = COND_TR; - - for (i = 0; i < type_size(param->type); i++) { - ir_to_mesa_emit_op1(ir, OPCODE_MOV, l, r); - l.index++; - r.index++; - } - } - - sig_iter.next(); - } - assert(!sig_iter.has_next()); - - /* Emit call instruction */ - call_inst = ir_to_mesa_emit_op1(ir, OPCODE_CAL, - ir_to_mesa_undef_dst, ir_to_mesa_undef); - call_inst->function = entry; - - /* Process out parameters. */ - sig_iter = sig->parameters.iterator(); - foreach_iter(exec_list_iterator, iter, *ir) { - ir_rvalue *param_rval = (ir_rvalue *)iter.get(); - ir_variable *param = (ir_variable *)sig_iter.get(); - - if (param->mode == ir_var_out || - param->mode == ir_var_inout) { - variable_storage *storage = find_variable_storage(param); - assert(storage); - - ir_to_mesa_src_reg r; - r.file = storage->file; - r.index = storage->index; - r.reladdr = NULL; - r.swizzle = SWIZZLE_NOOP; - r.negate = 0; - - param_rval->accept(this); - ir_to_mesa_dst_reg l = ir_to_mesa_dst_reg_from_src(this->result); - - for (i = 0; i < type_size(param->type); i++) { - ir_to_mesa_emit_op1(ir, OPCODE_MOV, l, r); - l.index++; - r.index++; - } - } - - sig_iter.next(); - } - assert(!sig_iter.has_next()); - - /* Process return value. */ - this->result = entry->return_reg; -} - -void -ir_to_mesa_visitor::visit(ir_texture *ir) -{ - ir_to_mesa_src_reg result_src, coord, lod_info, projector; - ir_to_mesa_dst_reg result_dst, coord_dst; - ir_to_mesa_instruction *inst = NULL; - prog_opcode opcode = OPCODE_NOP; - - ir->coordinate->accept(this); - - /* Put our coords in a temp. We'll need to modify them for shadow, - * projection, or LOD, so the only case we'd use it as is is if - * we're doing plain old texturing. Mesa IR optimization should - * handle cleaning up our mess in that case. - */ - coord = get_temp(glsl_type::vec4_type); - coord_dst = ir_to_mesa_dst_reg_from_src(coord); - ir_to_mesa_emit_op1(ir, OPCODE_MOV, coord_dst, - this->result); - - if (ir->projector) { - ir->projector->accept(this); - projector = this->result; - } - - /* Storage for our result. Ideally for an assignment we'd be using - * the actual storage for the result here, instead. - */ - result_src = get_temp(glsl_type::vec4_type); - result_dst = ir_to_mesa_dst_reg_from_src(result_src); - - switch (ir->op) { - case ir_tex: - opcode = OPCODE_TEX; - break; - case ir_txb: - opcode = OPCODE_TXB; - ir->lod_info.bias->accept(this); - lod_info = this->result; - break; - case ir_txl: - opcode = OPCODE_TXL; - ir->lod_info.lod->accept(this); - lod_info = this->result; - break; - case ir_txd: - case ir_txf: - assert(!"GLSL 1.30 features unsupported"); - break; - } - - if (ir->projector) { - if (opcode == OPCODE_TEX) { - /* Slot the projector in as the last component of the coord. */ - coord_dst.writemask = WRITEMASK_W; - ir_to_mesa_emit_op1(ir, OPCODE_MOV, coord_dst, projector); - coord_dst.writemask = WRITEMASK_XYZW; - opcode = OPCODE_TXP; - } else { - ir_to_mesa_src_reg coord_w = coord; - coord_w.swizzle = SWIZZLE_WWWW; - - /* For the other TEX opcodes there's no projective version - * since the last slot is taken up by lod info. Do the - * projective divide now. - */ - coord_dst.writemask = WRITEMASK_W; - ir_to_mesa_emit_op1(ir, OPCODE_RCP, coord_dst, projector); - - coord_dst.writemask = WRITEMASK_XYZ; - ir_to_mesa_emit_op2(ir, OPCODE_MUL, coord_dst, coord, coord_w); - - coord_dst.writemask = WRITEMASK_XYZW; - coord.swizzle = SWIZZLE_XYZW; - } - } - - if (ir->shadow_comparitor) { - /* Slot the shadow value in as the second to last component of the - * coord. - */ - ir->shadow_comparitor->accept(this); - coord_dst.writemask = WRITEMASK_Z; - ir_to_mesa_emit_op1(ir, OPCODE_MOV, coord_dst, this->result); - coord_dst.writemask = WRITEMASK_XYZW; - } - - if (opcode == OPCODE_TXL || opcode == OPCODE_TXB) { - /* Mesa IR stores lod or lod bias in the last channel of the coords. */ - coord_dst.writemask = WRITEMASK_W; - ir_to_mesa_emit_op1(ir, OPCODE_MOV, coord_dst, lod_info); - coord_dst.writemask = WRITEMASK_XYZW; - } - - inst = ir_to_mesa_emit_op1(ir, opcode, result_dst, coord); - - if (ir->shadow_comparitor) - inst->tex_shadow = GL_TRUE; - - inst->sampler = _mesa_get_sampler_uniform_value(ir->sampler, - this->shader_program, - this->prog); - - const glsl_type *sampler_type = ir->sampler->type; - - switch (sampler_type->sampler_dimensionality) { - case GLSL_SAMPLER_DIM_1D: - inst->tex_target = (sampler_type->sampler_array) - ? TEXTURE_1D_ARRAY_INDEX : TEXTURE_1D_INDEX; - break; - case GLSL_SAMPLER_DIM_2D: - 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; - break; - 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(!"Should not get here."); - } - - this->result = result_src; -} - -void -ir_to_mesa_visitor::visit(ir_return *ir) -{ - 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; - - l = ir_to_mesa_dst_reg_from_src(current_function->return_reg); - - for (i = 0; i < type_size(current_function->sig->return_type); i++) { - ir_to_mesa_emit_op1(ir, OPCODE_MOV, l, r); - l.index++; - r.index++; - } - } - - ir_to_mesa_emit_op0(ir, OPCODE_RET); -} - -void -ir_to_mesa_visitor::visit(ir_discard *ir) -{ - struct gl_fragment_program *fp = (struct gl_fragment_program *)this->prog; - - if (ir->condition) { - ir->condition->accept(this); - this->result.negate = ~this->result.negate; - ir_to_mesa_emit_op1(ir, OPCODE_KIL, ir_to_mesa_undef_dst, this->result); - } else { - ir_to_mesa_emit_op0(ir, OPCODE_KIL_NV); - } - - fp->UsesKill = GL_TRUE; -} - -void -ir_to_mesa_visitor::visit(ir_if *ir) -{ - ir_to_mesa_instruction *cond_inst, *if_inst, *else_inst = NULL; - ir_to_mesa_instruction *prev_inst; - - prev_inst = (ir_to_mesa_instruction *)this->instructions.get_tail(); - - ir->condition->accept(this); - assert(this->result.file != PROGRAM_UNDEFINED); - - if (this->options->EmitCondCodes) { - cond_inst = (ir_to_mesa_instruction *)this->instructions.get_tail(); - - /* See if we actually generated any instruction for generating - * the condition. If not, then cook up a move to a temp so we - * have something to set cond_update on. - */ - if (cond_inst == prev_inst) { - ir_to_mesa_src_reg temp = get_temp(glsl_type::bool_type); - cond_inst = ir_to_mesa_emit_op1(ir->condition, OPCODE_MOV, - ir_to_mesa_dst_reg_from_src(temp), - result); - } - cond_inst->cond_update = GL_TRUE; - - if_inst = ir_to_mesa_emit_op0(ir->condition, OPCODE_IF); - if_inst->dst_reg.cond_mask = COND_NE; - } else { - if_inst = ir_to_mesa_emit_op1(ir->condition, - OPCODE_IF, ir_to_mesa_undef_dst, - this->result); - } - - this->instructions.push_tail(if_inst); - - visit_exec_list(&ir->then_instructions, this); - - if (!ir->else_instructions.is_empty()) { - else_inst = ir_to_mesa_emit_op0(ir->condition, OPCODE_ELSE); - visit_exec_list(&ir->else_instructions, this); - } - - if_inst = ir_to_mesa_emit_op1(ir->condition, OPCODE_ENDIF, - ir_to_mesa_undef_dst, ir_to_mesa_undef); -} - -ir_to_mesa_visitor::ir_to_mesa_visitor() -{ - result.file = PROGRAM_UNDEFINED; - next_temp = 1; - next_signature_id = 1; - current_function = NULL; - mem_ctx = ralloc_context(NULL); -} - -ir_to_mesa_visitor::~ir_to_mesa_visitor() -{ - ralloc_free(mem_ctx); -} - -static struct prog_src_register -mesa_src_reg_from_ir_src_reg(ir_to_mesa_src_reg reg) -{ - struct prog_src_register mesa_reg; - - mesa_reg.File = reg.file; - assert(reg.index < (1 << INST_INDEX_BITS)); - mesa_reg.Index = reg.index; - mesa_reg.Swizzle = reg.swizzle; - mesa_reg.RelAddr = reg.reladdr != NULL; - 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; -} - -static void -set_branchtargets(ir_to_mesa_visitor *v, - struct prog_instruction *mesa_instructions, - int num_instructions) -{ - int if_count = 0, loop_count = 0; - int *if_stack, *loop_stack; - int if_stack_pos = 0, loop_stack_pos = 0; - int i, j; - - for (i = 0; i < num_instructions; i++) { - switch (mesa_instructions[i].Opcode) { - case OPCODE_IF: - if_count++; - break; - case OPCODE_BGNLOOP: - loop_count++; - break; - case OPCODE_BRK: - case OPCODE_CONT: - mesa_instructions[i].BranchTarget = -1; - break; - default: - break; - } - } - - if_stack = rzalloc_array(v->mem_ctx, int, if_count); - loop_stack = rzalloc_array(v->mem_ctx, int, loop_count); - - for (i = 0; i < num_instructions; i++) { - switch (mesa_instructions[i].Opcode) { - case OPCODE_IF: - if_stack[if_stack_pos] = i; - if_stack_pos++; - break; - case OPCODE_ELSE: - mesa_instructions[if_stack[if_stack_pos - 1]].BranchTarget = i; - if_stack[if_stack_pos - 1] = i; - break; - case OPCODE_ENDIF: - mesa_instructions[if_stack[if_stack_pos - 1]].BranchTarget = i; - if_stack_pos--; - break; - case OPCODE_BGNLOOP: - loop_stack[loop_stack_pos] = i; - loop_stack_pos++; - break; - case OPCODE_ENDLOOP: - loop_stack_pos--; - /* Rewrite any breaks/conts at this nesting level (haven't - * already had a BranchTarget assigned) to point to the end - * of the loop. - */ - for (j = loop_stack[loop_stack_pos]; j < i; j++) { - if (mesa_instructions[j].Opcode == OPCODE_BRK || - mesa_instructions[j].Opcode == OPCODE_CONT) { - if (mesa_instructions[j].BranchTarget == -1) { - mesa_instructions[j].BranchTarget = i; - } - } - } - /* The loop ends point at each other. */ - mesa_instructions[i].BranchTarget = loop_stack[loop_stack_pos]; - mesa_instructions[loop_stack[loop_stack_pos]].BranchTarget = i; - break; - case OPCODE_CAL: - foreach_iter(exec_list_iterator, iter, v->function_signatures) { - function_entry *entry = (function_entry *)iter.get(); - - if (entry->sig_id == mesa_instructions[i].BranchTarget) { - mesa_instructions[i].BranchTarget = entry->inst; - break; - } - } - break; - default: - break; - } - } -} - -static void -print_program(struct prog_instruction *mesa_instructions, - ir_instruction **mesa_instruction_annotation, - int num_instructions) -{ - ir_instruction *last_ir = NULL; - int i; - int indent = 0; - - for (i = 0; i < num_instructions; i++) { - struct prog_instruction *mesa_inst = mesa_instructions + i; - ir_instruction *ir = mesa_instruction_annotation[i]; - - fprintf(stdout, "%3d: ", i); - - if (last_ir != ir && ir) { - int j; - - for (j = 0; j < indent; j++) { - fprintf(stdout, " "); - } - ir->print(); - printf("\n"); - last_ir = ir; - - fprintf(stdout, " "); /* line number spacing. */ - } - - indent = _mesa_fprint_instruction_opt(stdout, mesa_inst, indent, - PROG_PRINT_DEBUG, NULL); - } -} - - -/** - * Count resources used by the given gpu program (number of texture - * samplers, etc). - */ -static void -count_resources(struct gl_program *prog) -{ - unsigned int i; - - prog->SamplersUsed = 0; - - for (i = 0; i < prog->NumInstructions; i++) { - struct prog_instruction *inst = &prog->Instructions[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; - } - } - } - - _mesa_update_shader_textures_used(prog); -} - - -/** - * Check if the given vertex/fragment/shader program is within the - * resource limits of the context (number of texture units, etc). - * If any of those checks fail, record a linker error. - * - * XXX more checks are needed... - */ -static void -check_resources(const struct gl_context *ctx, - struct gl_shader_program *shader_program, - struct gl_program *prog) -{ - switch (prog->Target) { - case GL_VERTEX_PROGRAM_ARB: - if (_mesa_bitcount(prog->SamplersUsed) > - ctx->Const.MaxVertexTextureImageUnits) { - fail_link(shader_program, "Too many vertex shader texture samplers"); - } - if (prog->Parameters->NumParameters > MAX_UNIFORMS) { - fail_link(shader_program, "Too many vertex shader constants"); - } - break; - case MESA_GEOMETRY_PROGRAM: - if (_mesa_bitcount(prog->SamplersUsed) > - ctx->Const.MaxGeometryTextureImageUnits) { - fail_link(shader_program, "Too many geometry shader texture samplers"); - } - if (prog->Parameters->NumParameters > - MAX_GEOMETRY_UNIFORM_COMPONENTS / 4) { - fail_link(shader_program, "Too many geometry shader constants"); - } - break; - case GL_FRAGMENT_PROGRAM_ARB: - if (_mesa_bitcount(prog->SamplersUsed) > - ctx->Const.MaxTextureImageUnits) { - fail_link(shader_program, "Too many fragment shader texture samplers"); - } - if (prog->Parameters->NumParameters > MAX_UNIFORMS) { - fail_link(shader_program, "Too many fragment shader constants"); - } - break; - default: - _mesa_problem(ctx, "unexpected program type in check_resources()"); - } -} - - - -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) -{ - struct uniform_sort *u1 = (struct uniform_sort *)a; - struct uniform_sort *u2 = (struct uniform_sort *)b; - - 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 -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; - - sorted_uniforms = ralloc_array(NULL, struct uniform_sort, - shader_program->Uniforms->NumUniforms); - - for (i = 0; i < shader_program->Uniforms->NumUniforms; i++) { - struct gl_uniform *uniform = shader_program->Uniforms->Uniforms + i; - int parameter_index = -1; - - switch (shader->Type) { - case GL_VERTEX_SHADER: - parameter_index = uniform->VertPos; - break; - case GL_FRAGMENT_SHADER: - parameter_index = uniform->FragPos; - break; - case GL_GEOMETRY_SHADER: - parameter_index = uniform->GeomPos; - 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++; - } - } - - 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); - } - } - } - - ralloc_free(sorted_uniforms); -} - -static void -set_uniform_initializer(struct gl_context *ctx, void *mem_ctx, - struct gl_shader_program *shader_program, - const char *name, const glsl_type *type, - ir_constant *val) -{ - if (type->is_record()) { - ir_constant *field_constant; - - field_constant = (ir_constant *)val->components.get_head(); - - for (unsigned int i = 0; i < type->length; i++) { - const glsl_type *field_type = type->fields.structure[i].type; - const char *field_name = ralloc_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 = ralloc_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(struct gl_context *ctx, - struct gl_shader_program *shader_program) -{ - void *mem_ctx = NULL; - - for (unsigned int i = 0; i < MESA_SHADER_TYPES; i++) { - struct gl_shader *shader = shader_program->_LinkedShaders[i]; - - if (shader == NULL) - continue; - - 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 = ralloc_context(NULL); - - set_uniform_initializer(ctx, mem_ctx, shader_program, var->name, - var->type, var->constant_value); - } - } - - ralloc_free(mem_ctx); -} - -/* - * On a basic block basis, tracks available PROGRAM_TEMPORARY register - * channels for copy propagation and updates following instructions to - * use the original versions. - * - * The ir_to_mesa_visitor lazily produces code assuming that this pass - * will occur. As an example, a TXP production before this pass: - * - * 0: MOV TEMP[1], INPUT[4].xyyy; - * 1: MOV TEMP[1].w, INPUT[4].wwww; - * 2: TXP TEMP[2], TEMP[1], texture[0], 2D; - * - * and after: - * - * 0: MOV TEMP[1], INPUT[4].xyyy; - * 1: MOV TEMP[1].w, INPUT[4].wwww; - * 2: TXP TEMP[2], INPUT[4].xyyw, texture[0], 2D; - * - * which allows for dead code elimination on TEMP[1]'s writes. - */ -void -ir_to_mesa_visitor::copy_propagate(void) -{ - ir_to_mesa_instruction **acp = rzalloc_array(mem_ctx, - ir_to_mesa_instruction *, - this->next_temp * 4); - int *acp_level = rzalloc_array(mem_ctx, int, this->next_temp * 4); - int level = 0; - - foreach_iter(exec_list_iterator, iter, this->instructions) { - ir_to_mesa_instruction *inst = (ir_to_mesa_instruction *)iter.get(); - - assert(inst->dst_reg.file != PROGRAM_TEMPORARY - || inst->dst_reg.index < this->next_temp); - - /* First, do any copy propagation possible into the src regs. */ - for (int r = 0; r < 3; r++) { - ir_to_mesa_instruction *first = NULL; - bool good = true; - int acp_base = inst->src_reg[r].index * 4; - - if (inst->src_reg[r].file != PROGRAM_TEMPORARY || - inst->src_reg[r].reladdr) - continue; - - /* See if we can find entries in the ACP consisting of MOVs - * from the same src register for all the swizzled channels - * of this src register reference. - */ - for (int i = 0; i < 4; i++) { - int src_chan = GET_SWZ(inst->src_reg[r].swizzle, i); - ir_to_mesa_instruction *copy_chan = acp[acp_base + src_chan]; - - if (!copy_chan) { - good = false; - break; - } - - assert(acp_level[acp_base + src_chan] <= level); - - if (!first) { - first = copy_chan; - } else { - if (first->src_reg[0].file != copy_chan->src_reg[0].file || - first->src_reg[0].index != copy_chan->src_reg[0].index) { - good = false; - break; - } - } - } - - if (good) { - /* We've now validated that we can copy-propagate to - * replace this src register reference. Do it. - */ - inst->src_reg[r].file = first->src_reg[0].file; - inst->src_reg[r].index = first->src_reg[0].index; - - int swizzle = 0; - for (int i = 0; i < 4; i++) { - int src_chan = GET_SWZ(inst->src_reg[r].swizzle, i); - ir_to_mesa_instruction *copy_inst = acp[acp_base + src_chan]; - swizzle |= (GET_SWZ(copy_inst->src_reg[0].swizzle, src_chan) << - (3 * i)); - } - inst->src_reg[r].swizzle = swizzle; - } - } - - switch (inst->op) { - case OPCODE_BGNLOOP: - case OPCODE_ENDLOOP: - /* End of a basic block, clear the ACP entirely. */ - memset(acp, 0, sizeof(*acp) * this->next_temp * 4); - break; - - case OPCODE_IF: - ++level; - break; - - case OPCODE_ENDIF: - case OPCODE_ELSE: - /* Clear all channels written inside the block from the ACP, but - * leaving those that were not touched. - */ - for (int r = 0; r < this->next_temp; r++) { - for (int c = 0; c < 4; c++) { - if (!acp[4 * r + c]) - continue; - - if (acp_level[4 * r + c] >= level) - acp[4 * r + c] = NULL; - } - } - if (inst->op == OPCODE_ENDIF) - --level; - break; - - default: - /* Continuing the block, clear any written channels from - * the ACP. - */ - if (inst->dst_reg.file == PROGRAM_TEMPORARY && inst->dst_reg.reladdr) { - /* Any temporary might be written, so no copy propagation - * across this instruction. - */ - memset(acp, 0, sizeof(*acp) * this->next_temp * 4); - } else if (inst->dst_reg.file == PROGRAM_OUTPUT && - inst->dst_reg.reladdr) { - /* Any output might be written, so no copy propagation - * from outputs across this instruction. - */ - for (int r = 0; r < this->next_temp; r++) { - for (int c = 0; c < 4; c++) { - if (!acp[4 * r + c]) - continue; - - if (acp[4 * r + c]->src_reg[0].file == PROGRAM_OUTPUT) - acp[4 * r + c] = NULL; - } - } - } else if (inst->dst_reg.file == PROGRAM_TEMPORARY || - inst->dst_reg.file == PROGRAM_OUTPUT) { - /* Clear where it's used as dst. */ - if (inst->dst_reg.file == PROGRAM_TEMPORARY) { - for (int c = 0; c < 4; c++) { - if (inst->dst_reg.writemask & (1 << c)) { - acp[4 * inst->dst_reg.index + c] = NULL; - } - } - } - - /* Clear where it's used as src. */ - for (int r = 0; r < this->next_temp; r++) { - for (int c = 0; c < 4; c++) { - if (!acp[4 * r + c]) - continue; - - int src_chan = GET_SWZ(acp[4 * r + c]->src_reg[0].swizzle, c); - - if (acp[4 * r + c]->src_reg[0].file == inst->dst_reg.file && - acp[4 * r + c]->src_reg[0].index == inst->dst_reg.index && - inst->dst_reg.writemask & (1 << src_chan)) - { - acp[4 * r + c] = NULL; - } - } - } - } - break; - } - - /* If this is a copy, add it to the ACP. */ - if (inst->op == OPCODE_MOV && - inst->dst_reg.file == PROGRAM_TEMPORARY && - !inst->dst_reg.reladdr && - !inst->saturate && - !inst->src_reg[0].reladdr && - !inst->src_reg[0].negate) { - for (int i = 0; i < 4; i++) { - if (inst->dst_reg.writemask & (1 << i)) { - acp[4 * inst->dst_reg.index + i] = inst; - acp_level[4 * inst->dst_reg.index + i] = level; - } - } - } - } - - ralloc_free(acp_level); - ralloc_free(acp); -} - - -/** - * Convert a shader's GLSL IR into a Mesa gl_program. - */ -static struct gl_program * -get_mesa_program(struct gl_context *ctx, - struct gl_shader_program *shader_program, - struct gl_shader *shader) -{ - ir_to_mesa_visitor v; - struct prog_instruction *mesa_instructions, *mesa_inst; - ir_instruction **mesa_instruction_annotation; - int i; - struct gl_program *prog; - 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: - target = GL_VERTEX_PROGRAM_ARB; - target_string = "vertex"; - break; - case GL_FRAGMENT_SHADER: - target = GL_FRAGMENT_PROGRAM_ARB; - target_string = "fragment"; - break; - case GL_GEOMETRY_SHADER: - target = GL_GEOMETRY_PROGRAM_NV; - target_string = "geometry"; - break; - default: - assert(!"should not be reached"); - return NULL; - } - - validate_ir_tree(shader->ir); - - prog = ctx->Driver.NewProgram(ctx, target, shader_program->Name); - if (!prog) - return NULL; - prog->Parameters = _mesa_new_parameter_list(); - prog->Varying = _mesa_new_parameter_list(); - prog->Attributes = _mesa_new_parameter_list(); - v.ctx = ctx; - v.prog = prog; - v.shader_program = shader_program; - v.options = options; - - add_uniforms_to_parameters_list(shader_program, shader, prog); - - /* Emit Mesa IR for main(). */ - visit_exec_list(shader->ir, &v); - v.ir_to_mesa_emit_op0(NULL, OPCODE_END); - - /* Now emit bodies for any functions that were used. */ - do { - progress = GL_FALSE; - - foreach_iter(exec_list_iterator, iter, v.function_signatures) { - function_entry *entry = (function_entry *)iter.get(); - - if (!entry->bgn_inst) { - v.current_function = entry; - - entry->bgn_inst = v.ir_to_mesa_emit_op0(NULL, OPCODE_BGNSUB); - entry->bgn_inst->function = entry; - - visit_exec_list(&entry->sig->body, &v); - - ir_to_mesa_instruction *last; - last = (ir_to_mesa_instruction *)v.instructions.get_tail(); - if (last->op != OPCODE_RET) - v.ir_to_mesa_emit_op0(NULL, OPCODE_RET); - - ir_to_mesa_instruction *end; - end = v.ir_to_mesa_emit_op0(NULL, OPCODE_ENDSUB); - end->function = entry; - - progress = GL_TRUE; - } - } - } while (progress); - - prog->NumTemporaries = v.next_temp; - - int num_instructions = 0; - foreach_iter(exec_list_iterator, iter, v.instructions) { - num_instructions++; - } - - mesa_instructions = - (struct prog_instruction *)calloc(num_instructions, - sizeof(*mesa_instructions)); - mesa_instruction_annotation = ralloc_array(v.mem_ctx, ir_instruction *, - num_instructions); - - v.copy_propagate(); - - /* Convert ir_mesa_instructions into prog_instructions. - */ - mesa_inst = mesa_instructions; - i = 0; - foreach_iter(exec_list_iterator, iter, v.instructions) { - const ir_to_mesa_instruction *inst = (ir_to_mesa_instruction *)iter.get(); - - mesa_inst->Opcode = inst->op; - mesa_inst->CondUpdate = inst->cond_update; - if (inst->saturate) - mesa_inst->SaturateMode = SATURATE_ZERO_ONE; - mesa_inst->DstReg.File = inst->dst_reg.file; - mesa_inst->DstReg.Index = inst->dst_reg.index; - mesa_inst->DstReg.CondMask = inst->dst_reg.cond_mask; - mesa_inst->DstReg.WriteMask = inst->dst_reg.writemask; - mesa_inst->DstReg.RelAddr = inst->dst_reg.reladdr != NULL; - mesa_inst->SrcReg[0] = mesa_src_reg_from_ir_src_reg(inst->src_reg[0]); - mesa_inst->SrcReg[1] = mesa_src_reg_from_ir_src_reg(inst->src_reg[1]); - mesa_inst->SrcReg[2] = mesa_src_reg_from_ir_src_reg(inst->src_reg[2]); - mesa_inst->TexSrcUnit = inst->sampler; - mesa_inst->TexSrcTarget = inst->tex_target; - mesa_inst->TexShadow = inst->tex_shadow; - mesa_instruction_annotation[i] = inst->ir; - - /* Set IndirectRegisterFiles. */ - if (mesa_inst->DstReg.RelAddr) - prog->IndirectRegisterFiles |= 1 << mesa_inst->DstReg.File; - - /* Update program's bitmask of indirectly accessed register files */ - 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) { - case OPCODE_BGNSUB: - inst->function->inst = i; - mesa_inst->Comment = strdup(inst->function->sig->function_name()); - break; - case OPCODE_ENDSUB: - mesa_inst->Comment = strdup(inst->function->sig->function_name()); - break; - case OPCODE_CAL: - mesa_inst->BranchTarget = inst->function->sig_id; /* rewritten later */ - break; - case OPCODE_ARL: - prog->NumAddressRegs = 1; - break; - default: - break; - } - - mesa_inst++; - i++; - - if (!shader_program->LinkStatus) - break; - } - - if (!shader_program->LinkStatus) { - free(mesa_instructions); - _mesa_reference_program(ctx, &shader->Program, NULL); - return NULL; - } - - set_branchtargets(&v, mesa_instructions, num_instructions); - - if (ctx->Shader.Flags & GLSL_DUMP) { - 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); - } - - prog->Instructions = mesa_instructions; - prog->NumInstructions = num_instructions; - - do_set_program_inouts(shader->ir, prog); - count_resources(prog); - - check_resources(ctx, shader_program, prog); - - _mesa_reference_program(ctx, &shader->Program, prog); - - if ((ctx->Shader.Flags & GLSL_NO_OPT) == 0) { - _mesa_optimize_program(ctx, prog); - } - - return prog; -} - -extern "C" { - -/** - * Link a shader. - * Called via ctx->Driver.LinkShader() - * This actually involves converting GLSL IR into Mesa gl_programs with - * code lowering and other optimizations. - */ -GLboolean -_mesa_ir_link_shader(struct gl_context *ctx, struct gl_shader_program *prog) -{ - assert(prog->LinkStatus); - - for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) { - if (prog->_LinkedShaders[i] == NULL) - continue; - - bool progress; - exec_list *ir = prog->_LinkedShaders[i]->ir; - const 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); - lower_instructions(ir, (MOD_TO_FRACT | DIV_TO_MUL_RCP | EXP_TO_EXP2 - | LOG_TO_LOG2 - | ((options->EmitNoPow) ? POW_TO_EXP2 : 0))); - - progress = do_lower_jumps(ir, true, true, options->EmitNoMainReturn, options->EmitNoCont, options->EmitNoLoops) || progress; - - progress = do_common_optimization(ir, true, options->MaxUnrollIterations) || progress; - - progress = lower_quadop_vector(ir, true) || progress; - - if (options->EmitNoIfs) { - progress = lower_discard(ir) || progress; - progress = lower_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 < MESA_SHADER_TYPES; i++) { - struct gl_program *linked_prog; - - if (prog->_LinkedShaders[i] == NULL) - continue; - - linked_prog = get_mesa_program(ctx, prog, prog->_LinkedShaders[i]); - - if (linked_prog) { - bool ok = true; - - 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; - case GL_GEOMETRY_SHADER: - _mesa_reference_geomprog(ctx, &prog->GeometryProgram, - (struct gl_geometry_program *)linked_prog); - ok = ctx->Driver.ProgramStringNotify(ctx, GL_GEOMETRY_PROGRAM_NV, - linked_prog); - break; - } - if (!ok) { - return GL_FALSE; - } - } - - _mesa_reference_program(ctx, &linked_prog, NULL); - } - - return GL_TRUE; -} - - -/** - * Compile a GLSL shader. Called via glCompileShader(). - */ -void -_mesa_glsl_compile_shader(struct gl_context *ctx, struct gl_shader *shader) -{ - struct _mesa_glsl_parse_state *state = - 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->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); - _mesa_glsl_parse(state); - _mesa_glsl_lexer_dtor(state); - } - - ralloc_free(shader->ir); - shader->ir = new(shader) exec_list; - if (!state->error && !state->translation_unit.is_empty()) - _mesa_ast_to_hir(shader->ir, state); - - if (!state->error && !shader->ir->is_empty()) { - validate_ir_tree(shader->ir); - - /* 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); - } - - shader->symbols = state->symbols; - - shader->CompileStatus = !state->error; - shader->InfoLog = state->info_log; - shader->Version = state->language_version; - memcpy(shader->builtins_to_link, state->builtins_to_link, - sizeof(shader->builtins_to_link[0]) * state->num_builtins_to_link); - shader->num_builtins_to_link = state->num_builtins_to_link; - - if (ctx->Shader.Flags & GLSL_LOG) { - _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->ir); - - ralloc_free(state); -} - - -/** - * Link a GLSL shader program. Called via glLinkProgram(). - */ -void -_mesa_glsl_link_shader(struct gl_context *ctx, struct gl_shader_program *prog) -{ - unsigned int i; - - _mesa_clear_shader_program_data(ctx, prog); - - prog->LinkStatus = GL_TRUE; - - for (i = 0; i < prog->NumShaders; i++) { - if (!prog->Shaders[i]->CompileStatus) { - fail_link(prog, "linking with uncompiled shader"); - prog->LinkStatus = GL_FALSE; - } - } - - prog->Varying = _mesa_new_parameter_list(); - _mesa_reference_vertprog(ctx, &prog->VertexProgram, NULL); - _mesa_reference_fragprog(ctx, &prog->FragmentProgram, NULL); - _mesa_reference_geomprog(ctx, &prog->GeometryProgram, NULL); - - if (prog->LinkStatus) { - link_shaders(ctx, prog); - } - - if (prog->LinkStatus) { - 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); - } - } -} - -} /* extern "C" */ +/* + * Copyright (C) 2005-2007 Brian Paul All Rights Reserved. + * Copyright (C) 2008 VMware, Inc. All Rights Reserved. + * 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_to_mesa.cpp + * + * Translate GLSL IR to Mesa's gl_program representation. + */ + +#include +#include "main/compiler.h" +#include "ir.h" +#include "ir_visitor.h" +#include "ir_print_visitor.h" +#include "ir_expression_flattening.h" +#include "glsl_types.h" +#include "glsl_parser_extras.h" +#include "../glsl/program.h" +#include "ir_optimization.h" +#include "ast.h" + +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); + +/** + * This struct is a corresponding struct to Mesa prog_src_register, with + * wider fields. + */ +typedef struct ir_to_mesa_src_reg { + ir_to_mesa_src_reg(int file, int index, const glsl_type *type) + { + this->file = (gl_register_file) file; + this->index = index; + if (type && (type->is_scalar() || type->is_vector() || type->is_matrix())) + this->swizzle = swizzle_for_size(type->vector_elements); + else + this->swizzle = SWIZZLE_XYZW; + this->negate = 0; + this->reladdr = NULL; + } + + ir_to_mesa_src_reg() + { + this->file = PROGRAM_UNDEFINED; + this->index = 0; + this->swizzle = 0; + this->negate = 0; + this->reladdr = NULL; + } + + gl_register_file file; /**< PROGRAM_* from Mesa */ + int index; /**< temporary index, VERT_ATTRIB_*, FRAG_ATTRIB_*, etc. */ + GLuint swizzle; /**< SWIZZLE_XYZWONEZERO swizzles from Mesa. */ + int negate; /**< NEGATE_XYZW mask from mesa */ + /** Register index should be offset by the integer in this reg. */ + ir_to_mesa_src_reg *reladdr; +} ir_to_mesa_src_reg; + +typedef struct ir_to_mesa_dst_reg { + int file; /**< PROGRAM_* from Mesa */ + int index; /**< temporary index, VERT_ATTRIB_*, FRAG_ATTRIB_*, etc. */ + int writemask; /**< Bitfield of WRITEMASK_[XYZW] */ + GLuint cond_mask:4; + /** Register index should be offset by the integer in this reg. */ + ir_to_mesa_src_reg *reladdr; +} ir_to_mesa_dst_reg; + +extern ir_to_mesa_src_reg ir_to_mesa_undef; + +class ir_to_mesa_instruction : public exec_node { +public: + /* Callers of this ralloc-based new need not call delete. It's + * easier to just ralloc_free 'ctx' (or any of its ancestors). */ + static void* operator new(size_t size, void *ctx) + { + void *node; + + node = rzalloc_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]; + /** Pointer to the ir source this tree came from for debugging */ + ir_instruction *ir; + GLboolean cond_update; + bool saturate; + int sampler; /**< sampler index */ + int tex_target; /**< One of TEXTURE_*_INDEX */ + GLboolean tex_shadow; + + class function_entry *function; /* Set on OPCODE_CAL or OPCODE_BGNSUB */ +}; + +class variable_storage : public exec_node { +public: + variable_storage(ir_variable *var, gl_register_file file, int index) + : file(file), index(index), var(var) + { + /* empty */ + } + + gl_register_file file; + int index; + ir_variable *var; /* variable that maps to this, if any */ +}; + +class function_entry : public exec_node { +public: + ir_function_signature *sig; + + /** + * identifier of this function signature used by the program. + * + * At the point that Mesa instructions for function calls are + * generated, we don't know the address of the first instruction of + * the function body. So we make the BranchTarget that is called a + * small integer and rewrite them during set_branchtargets(). + */ + int sig_id; + + /** + * Pointer to first instruction of the function body. + * + * Set during function body emits after main() is processed. + */ + ir_to_mesa_instruction *bgn_inst; + + /** + * Index of the first instruction of the function body in actual + * Mesa IR. + * + * Set after convertion from ir_to_mesa_instruction to prog_instruction. + */ + int inst; + + /** Storage for the return value. */ + ir_to_mesa_src_reg return_reg; +}; + +class ir_to_mesa_visitor : public ir_visitor { +public: + ir_to_mesa_visitor(); + ~ir_to_mesa_visitor(); + + function_entry *current_function; + + struct gl_context *ctx; + struct gl_program *prog; + struct gl_shader_program *shader_program; + struct gl_shader_compiler_options *options; + + int next_temp; + + variable_storage *find_variable_storage(ir_variable *var); + + function_entry *get_function_signature(ir_function_signature *sig); + + ir_to_mesa_src_reg get_temp(const glsl_type *type); + void reladdr_to_temp(ir_instruction *ir, + ir_to_mesa_src_reg *reg, int *num_reladdr); + + struct ir_to_mesa_src_reg src_reg_for_float(float val); + + /** + * \name Visit methods + * + * As typical for the visitor pattern, there must be one \c visit method for + * each concrete subclass of \c ir_instruction. Virtual base classes within + * the hierarchy should not have \c visit methods. + */ + /*@{*/ + virtual void visit(ir_variable *); + virtual void visit(ir_loop *); + virtual void visit(ir_loop_jump *); + virtual void visit(ir_function_signature *); + virtual void visit(ir_function *); + virtual void visit(ir_expression *); + virtual void visit(ir_swizzle *); + virtual void visit(ir_dereference_variable *); + virtual void visit(ir_dereference_array *); + virtual void visit(ir_dereference_record *); + virtual void visit(ir_assignment *); + virtual void visit(ir_constant *); + virtual void visit(ir_call *); + virtual void visit(ir_return *); + virtual void visit(ir_discard *); + virtual void visit(ir_texture *); + virtual void visit(ir_if *); + /*@}*/ + + struct ir_to_mesa_src_reg result; + + /** List of variable_storage */ + exec_list variables; + + /** List of function_entry */ + exec_list function_signatures; + int next_signature_id; + + /** List of ir_to_mesa_instruction */ + exec_list instructions; + + ir_to_mesa_instruction *ir_to_mesa_emit_op0(ir_instruction *ir, + enum prog_opcode op); + + ir_to_mesa_instruction *ir_to_mesa_emit_op1(ir_instruction *ir, + enum prog_opcode op, + ir_to_mesa_dst_reg dst, + ir_to_mesa_src_reg src0); + + ir_to_mesa_instruction *ir_to_mesa_emit_op2(ir_instruction *ir, + enum prog_opcode op, + ir_to_mesa_dst_reg dst, + ir_to_mesa_src_reg src0, + ir_to_mesa_src_reg src1); + + ir_to_mesa_instruction *ir_to_mesa_emit_op3(ir_instruction *ir, + enum prog_opcode op, + ir_to_mesa_dst_reg dst, + ir_to_mesa_src_reg src0, + ir_to_mesa_src_reg src1, + ir_to_mesa_src_reg src2); + + /** + * Emit the correct dot-product instruction for the type of arguments + * + * \sa ir_to_mesa_emit_op2 + */ + void ir_to_mesa_emit_dp(ir_instruction *ir, + ir_to_mesa_dst_reg dst, + ir_to_mesa_src_reg src0, + ir_to_mesa_src_reg src1, + unsigned elements); + + void ir_to_mesa_emit_scalar_op1(ir_instruction *ir, + enum prog_opcode op, + ir_to_mesa_dst_reg dst, + ir_to_mesa_src_reg src0); + + void ir_to_mesa_emit_scalar_op2(ir_instruction *ir, + enum prog_opcode op, + ir_to_mesa_dst_reg dst, + ir_to_mesa_src_reg src0, + ir_to_mesa_src_reg src1); + + void emit_scs(ir_instruction *ir, enum prog_opcode op, + ir_to_mesa_dst_reg dst, + const ir_to_mesa_src_reg &src); + + GLboolean try_emit_mad(ir_expression *ir, + int mul_operand); + GLboolean try_emit_sat(ir_expression *ir); + + void emit_swz(ir_expression *ir); + + bool process_move_condition(ir_rvalue *ir); + + void copy_propagate(void); + + void *mem_ctx; +}; + +ir_to_mesa_src_reg ir_to_mesa_undef = ir_to_mesa_src_reg(PROGRAM_UNDEFINED, 0, NULL); + +ir_to_mesa_dst_reg ir_to_mesa_undef_dst = { + PROGRAM_UNDEFINED, 0, SWIZZLE_NOOP, COND_TR, NULL, +}; + +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); + ralloc_vasprintf_append(&prog->InfoLog, fmt, args); + va_end(args); + + prog->LinkStatus = GL_FALSE; +} + +static int +swizzle_for_size(int size) +{ + int size_swizzles[4] = { + MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_X, SWIZZLE_X, SWIZZLE_X), + MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Y, SWIZZLE_Y), + MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_Z), + MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_W), + }; + + assert((size >= 1) && (size <= 4)); + return size_swizzles[size - 1]; +} + +ir_to_mesa_instruction * +ir_to_mesa_visitor::ir_to_mesa_emit_op3(ir_instruction *ir, + enum prog_opcode op, + ir_to_mesa_dst_reg dst, + ir_to_mesa_src_reg src0, + ir_to_mesa_src_reg src1, + ir_to_mesa_src_reg src2) +{ + ir_to_mesa_instruction *inst = new(mem_ctx) ir_to_mesa_instruction(); + int num_reladdr = 0; + + /* If we have to do relative addressing, we want to load the ARL + * reg directly for one of the regs, and preload the other reladdr + * sources into temps. + */ + num_reladdr += dst.reladdr != NULL; + num_reladdr += src0.reladdr != NULL; + num_reladdr += src1.reladdr != NULL; + num_reladdr += src2.reladdr != NULL; + + reladdr_to_temp(ir, &src2, &num_reladdr); + reladdr_to_temp(ir, &src1, &num_reladdr); + reladdr_to_temp(ir, &src0, &num_reladdr); + + if (dst.reladdr) { + ir_to_mesa_emit_op1(ir, OPCODE_ARL, ir_to_mesa_address_reg, + *dst.reladdr); + + num_reladdr--; + } + assert(num_reladdr == 0); + + inst->op = op; + inst->dst_reg = dst; + inst->src_reg[0] = src0; + inst->src_reg[1] = src1; + inst->src_reg[2] = src2; + inst->ir = ir; + + inst->function = NULL; + + this->instructions.push_tail(inst); + + return inst; +} + + +ir_to_mesa_instruction * +ir_to_mesa_visitor::ir_to_mesa_emit_op2(ir_instruction *ir, + enum prog_opcode op, + ir_to_mesa_dst_reg dst, + ir_to_mesa_src_reg src0, + ir_to_mesa_src_reg src1) +{ + return ir_to_mesa_emit_op3(ir, op, dst, src0, src1, ir_to_mesa_undef); +} + +ir_to_mesa_instruction * +ir_to_mesa_visitor::ir_to_mesa_emit_op1(ir_instruction *ir, + enum prog_opcode op, + 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); +} + +ir_to_mesa_instruction * +ir_to_mesa_visitor::ir_to_mesa_emit_op0(ir_instruction *ir, + enum prog_opcode op) +{ + return ir_to_mesa_emit_op3(ir, op, ir_to_mesa_undef_dst, + ir_to_mesa_undef, + ir_to_mesa_undef, + ir_to_mesa_undef); +} + +void +ir_to_mesa_visitor::ir_to_mesa_emit_dp(ir_instruction *ir, + ir_to_mesa_dst_reg dst, + ir_to_mesa_src_reg src0, + ir_to_mesa_src_reg src1, + unsigned elements) +{ + static const gl_inst_opcode dot_opcodes[] = { + OPCODE_DP2, OPCODE_DP3, OPCODE_DP4 + }; + + ir_to_mesa_emit_op3(ir, dot_opcodes[elements - 2], + dst, src0, src1, ir_to_mesa_undef); +} + +inline ir_to_mesa_dst_reg +ir_to_mesa_dst_reg_from_src(ir_to_mesa_src_reg reg) +{ + ir_to_mesa_dst_reg dst_reg; + + dst_reg.file = reg.file; + dst_reg.index = reg.index; + dst_reg.writemask = WRITEMASK_XYZW; + dst_reg.cond_mask = COND_TR; + dst_reg.reladdr = reg.reladdr; + + return dst_reg; +} + +inline ir_to_mesa_src_reg +ir_to_mesa_src_reg_from_dst(ir_to_mesa_dst_reg reg) +{ + return ir_to_mesa_src_reg(reg.file, reg.index, NULL); +} + +/** + * Emits Mesa scalar opcodes to produce unique answers across channels. + * + * Some Mesa opcodes are scalar-only, like ARB_fp/vp. The src X + * channel determines the result across all channels. So to do a vec4 + * of this operation, we want to emit a scalar per source channel used + * to produce dest channels. + */ +void +ir_to_mesa_visitor::ir_to_mesa_emit_scalar_op2(ir_instruction *ir, + enum prog_opcode op, + ir_to_mesa_dst_reg dst, + ir_to_mesa_src_reg orig_src0, + ir_to_mesa_src_reg orig_src1) +{ + int i, j; + int done_mask = ~dst.writemask; + + /* Mesa RCP is a scalar operation splatting results to all channels, + * like ARB_fp/vp. So emit as many RCPs as necessary to cover our + * dst channels. + */ + for (i = 0; i < 4; i++) { + GLuint this_mask = (1 << i); + ir_to_mesa_instruction *inst; + ir_to_mesa_src_reg src0 = orig_src0; + ir_to_mesa_src_reg src1 = orig_src1; + + if (done_mask & this_mask) + continue; + + GLuint src0_swiz = GET_SWZ(src0.swizzle, i); + GLuint src1_swiz = GET_SWZ(src1.swizzle, i); + for (j = i + 1; j < 4; j++) { + /* If there is another enabled component in the destination that is + * derived from the same inputs, generate its value on this pass as + * well. + */ + if (!(done_mask & (1 << j)) && + GET_SWZ(src0.swizzle, j) == src0_swiz && + GET_SWZ(src1.swizzle, j) == src1_swiz) { + this_mask |= (1 << j); + } + } + src0.swizzle = MAKE_SWIZZLE4(src0_swiz, src0_swiz, + src0_swiz, src0_swiz); + src1.swizzle = MAKE_SWIZZLE4(src1_swiz, src1_swiz, + src1_swiz, src1_swiz); + + inst = ir_to_mesa_emit_op2(ir, op, + dst, + src0, + src1); + inst->dst_reg.writemask = this_mask; + done_mask |= this_mask; + } +} + +void +ir_to_mesa_visitor::ir_to_mesa_emit_scalar_op1(ir_instruction *ir, + enum prog_opcode op, + ir_to_mesa_dst_reg dst, + ir_to_mesa_src_reg src0) +{ + ir_to_mesa_src_reg undef = ir_to_mesa_undef; + + undef.swizzle = SWIZZLE_XXXX; + + ir_to_mesa_emit_scalar_op2(ir, op, dst, src0, undef); +} + +/** + * Emit an OPCODE_SCS instruction + * + * The \c SCS opcode functions a bit differently than the other Mesa (or + * ARB_fragment_program) opcodes. Instead of splatting its result across all + * four components of the destination, it writes one value to the \c x + * component and another value to the \c y component. + * + * \param ir IR instruction being processed + * \param op Either \c OPCODE_SIN or \c OPCODE_COS depending on which + * value is desired. + * \param dst Destination register + * \param src Source register + */ +void +ir_to_mesa_visitor::emit_scs(ir_instruction *ir, enum prog_opcode op, + ir_to_mesa_dst_reg dst, + const ir_to_mesa_src_reg &src) +{ + /* Vertex programs cannot use the SCS opcode. + */ + if (this->prog->Target == GL_VERTEX_PROGRAM_ARB) { + ir_to_mesa_emit_scalar_op1(ir, op, dst, src); + return; + } + + const unsigned component = (op == OPCODE_SIN) ? 0 : 1; + const unsigned scs_mask = (1U << component); + int done_mask = ~dst.writemask; + ir_to_mesa_src_reg tmp; + + assert(op == OPCODE_SIN || op == OPCODE_COS); + + /* If there are compnents in the destination that differ from the component + * that will be written by the SCS instrution, we'll need a temporary. + */ + if (scs_mask != unsigned(dst.writemask)) { + tmp = get_temp(glsl_type::vec4_type); + } + + for (unsigned i = 0; i < 4; i++) { + unsigned this_mask = (1U << i); + ir_to_mesa_src_reg src0 = src; + + if ((done_mask & this_mask) != 0) + continue; + + /* The source swizzle specified which component of the source generates + * sine / cosine for the current component in the destination. The SCS + * instruction requires that this value be swizzle to the X component. + * Replace the current swizzle with a swizzle that puts the source in + * the X component. + */ + unsigned src0_swiz = GET_SWZ(src.swizzle, i); + + src0.swizzle = MAKE_SWIZZLE4(src0_swiz, src0_swiz, + src0_swiz, src0_swiz); + for (unsigned j = i + 1; j < 4; j++) { + /* If there is another enabled component in the destination that is + * derived from the same inputs, generate its value on this pass as + * well. + */ + if (!(done_mask & (1 << j)) && + GET_SWZ(src0.swizzle, j) == src0_swiz) { + this_mask |= (1 << j); + } + } + + if (this_mask != scs_mask) { + ir_to_mesa_instruction *inst; + ir_to_mesa_dst_reg tmp_dst = ir_to_mesa_dst_reg_from_src(tmp); + + /* Emit the SCS instruction. + */ + inst = ir_to_mesa_emit_op1(ir, OPCODE_SCS, tmp_dst, src0); + inst->dst_reg.writemask = scs_mask; + + /* Move the result of the SCS instruction to the desired location in + * the destination. + */ + tmp.swizzle = MAKE_SWIZZLE4(component, component, + component, component); + inst = ir_to_mesa_emit_op1(ir, OPCODE_SCS, dst, tmp); + inst->dst_reg.writemask = this_mask; + } else { + /* Emit the SCS instruction to write directly to the destination. + */ + ir_to_mesa_instruction *inst = + ir_to_mesa_emit_op1(ir, OPCODE_SCS, dst, src0); + inst->dst_reg.writemask = scs_mask; + } + + done_mask |= this_mask; + } +} + +struct ir_to_mesa_src_reg +ir_to_mesa_visitor::src_reg_for_float(float val) +{ + ir_to_mesa_src_reg src_reg(PROGRAM_CONSTANT, -1, NULL); + + src_reg.index = _mesa_add_unnamed_constant(this->prog->Parameters, + &val, 1, &src_reg.swizzle); + + return src_reg; +} + +static int +type_size(const struct glsl_type *type) +{ + unsigned int i; + int size; + + switch (type->base_type) { + case GLSL_TYPE_UINT: + case GLSL_TYPE_INT: + case GLSL_TYPE_FLOAT: + case GLSL_TYPE_BOOL: + if (type->is_matrix()) { + return type->matrix_columns; + } else { + /* Regardless of size of vector, it gets a vec4. This is bad + * packing for things like floats, but otherwise arrays become a + * mess. Hopefully a later pass over the code can pack scalars + * down if appropriate. + */ + return 1; + } + case GLSL_TYPE_ARRAY: + assert(type->length > 0); + return type_size(type->fields.array) * type->length; + case GLSL_TYPE_STRUCT: + size = 0; + for (i = 0; i < type->length; i++) { + 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; + } +} + +/** + * In the initial pass of codegen, we assign temporary numbers to + * intermediate results. (not SSA -- variable assignments will reuse + * storage). Actual register allocation for the Mesa VM occurs in a + * pass over the Mesa IR later. + */ +ir_to_mesa_src_reg +ir_to_mesa_visitor::get_temp(const glsl_type *type) +{ + ir_to_mesa_src_reg src_reg; + int swizzle[4]; + int i; + + src_reg.file = PROGRAM_TEMPORARY; + src_reg.index = next_temp; + src_reg.reladdr = NULL; + next_temp += type_size(type); + + if (type->is_array() || type->is_record()) { + src_reg.swizzle = SWIZZLE_NOOP; + } else { + for (i = 0; i < type->vector_elements; i++) + swizzle[i] = i; + for (; i < 4; i++) + swizzle[i] = type->vector_elements - 1; + src_reg.swizzle = MAKE_SWIZZLE4(swizzle[0], swizzle[1], + swizzle[2], swizzle[3]); + } + src_reg.negate = 0; + + return src_reg; +} + +variable_storage * +ir_to_mesa_visitor::find_variable_storage(ir_variable *var) +{ + + variable_storage *entry; + + foreach_iter(exec_list_iterator, iter, this->variables) { + entry = (variable_storage *)iter.get(); + + if (entry->var == var) + return entry; + } + + return NULL; +} + +void +ir_to_mesa_visitor::visit(ir_variable *ir) +{ + if (strcmp(ir->name, "gl_FragCoord") == 0) { + struct gl_fragment_program *fp = (struct gl_fragment_program *)this->prog; + + fp->OriginUpperLeft = ir->origin_upper_left; + fp->PixelCenterInteger = ir->pixel_center_integer; + + } else if (strcmp(ir->name, "gl_FragDepth") == 0) { + struct gl_fragment_program *fp = (struct gl_fragment_program *)this->prog; + switch (ir->depth_layout) { + case ir_depth_layout_none: + fp->FragDepthLayout = FRAG_DEPTH_LAYOUT_NONE; + break; + case ir_depth_layout_any: + fp->FragDepthLayout = FRAG_DEPTH_LAYOUT_ANY; + break; + case ir_depth_layout_greater: + fp->FragDepthLayout = FRAG_DEPTH_LAYOUT_GREATER; + break; + case ir_depth_layout_less: + fp->FragDepthLayout = FRAG_DEPTH_LAYOUT_LESS; + break; + case ir_depth_layout_unchanged: + fp->FragDepthLayout = FRAG_DEPTH_LAYOUT_UNCHANGED; + break; + default: + assert(0); + break; + } + } + + if (ir->mode == ir_var_uniform && strncmp(ir->name, "gl_", 3) == 0) { + unsigned int i; + const ir_state_slot *const slots = ir->state_slots; + assert(ir->state_slots != NULL); + + /* 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 < ir->num_state_slots; i++) { + if (slots[i].swizzle != SWIZZLE_XYZW) { + break; + } + } + + struct variable_storage *storage; + ir_to_mesa_dst_reg dst; + if (i == ir->num_state_slots) { + /* 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 { + /* The variable_storage constructor allocates slots based on the size + * of the type. However, this had better match the number of state + * elements that we're going to copy into the new temporary. + */ + assert(ir->num_state_slots == type_size(ir->type)); + + 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 (unsigned int i = 0; i < ir->num_state_slots; i++) { + int index = _mesa_add_state_reference(this->prog->Parameters, + (gl_state_index *)slots[i].tokens); + + if (storage->file == PROGRAM_STATE_VAR) { + if (storage->index == -1) { + storage->index = index; + } else { + assert(index == storage->index + (int)i); + } + } else { + ir_to_mesa_src_reg src(PROGRAM_STATE_VAR, index, NULL); + src.swizzle = slots[i].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 + ir->num_state_slots) { + 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) +{ + 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); +} + +void +ir_to_mesa_visitor::visit(ir_loop_jump *ir) +{ + switch (ir->mode) { + case ir_loop_jump::jump_break: + ir_to_mesa_emit_op0(NULL, OPCODE_BRK); + break; + case ir_loop_jump::jump_continue: + ir_to_mesa_emit_op0(NULL, OPCODE_CONT); + break; + } +} + + +void +ir_to_mesa_visitor::visit(ir_function_signature *ir) +{ + assert(0); + (void)ir; +} + +void +ir_to_mesa_visitor::visit(ir_function *ir) +{ + /* Ignore function bodies other than main() -- we shouldn't see calls to + * them since they should all be inlined before we get to ir_to_mesa. + */ + if (strcmp(ir->name, "main") == 0) { + const ir_function_signature *sig; + exec_list empty; + + sig = ir->matching_signature(&empty); + + assert(sig); + + foreach_iter(exec_list_iterator, iter, sig->body) { + ir_instruction *ir = (ir_instruction *)iter.get(); + + ir->accept(this); + } + } +} + +GLboolean +ir_to_mesa_visitor::try_emit_mad(ir_expression *ir, int mul_operand) +{ + int nonmul_operand = 1 - mul_operand; + ir_to_mesa_src_reg a, b, c; + + ir_expression *expr = ir->operands[mul_operand]->as_expression(); + if (!expr || expr->operation != ir_binop_mul) + return false; + + expr->operands[0]->accept(this); + a = this->result; + expr->operands[1]->accept(this); + b = this->result; + ir->operands[nonmul_operand]->accept(this); + c = this->result; + + this->result = get_temp(ir->type); + ir_to_mesa_emit_op3(ir, OPCODE_MAD, + ir_to_mesa_dst_reg_from_src(this->result), a, b, c); + + return true; +} + +GLboolean +ir_to_mesa_visitor::try_emit_sat(ir_expression *ir) +{ + /* Saturates were only introduced to vertex programs in + * NV_vertex_program3, so don't give them to drivers in the VP. + */ + if (this->prog->Target == GL_VERTEX_PROGRAM_ARB) + return false; + + ir_rvalue *sat_src = ir->as_rvalue_to_saturate(); + if (!sat_src) + return false; + + sat_src->accept(this); + ir_to_mesa_src_reg src = this->result; + + this->result = get_temp(ir->type); + ir_to_mesa_instruction *inst; + inst = ir_to_mesa_emit_op1(ir, OPCODE_MOV, + ir_to_mesa_dst_reg_from_src(this->result), + src); + inst->saturate = true; + + return true; +} + +void +ir_to_mesa_visitor::reladdr_to_temp(ir_instruction *ir, + ir_to_mesa_src_reg *reg, int *num_reladdr) +{ + if (!reg->reladdr) + return; + + ir_to_mesa_emit_op1(ir, OPCODE_ARL, ir_to_mesa_address_reg, *reg->reladdr); + + if (*num_reladdr != 1) { + ir_to_mesa_src_reg temp = get_temp(glsl_type::vec4_type); + + ir_to_mesa_emit_op1(ir, OPCODE_MOV, + ir_to_mesa_dst_reg_from_src(temp), *reg); + *reg = temp; + } + + (*num_reladdr)--; +} + +void +ir_to_mesa_visitor::emit_swz(ir_expression *ir) +{ + /* Assume that the vector operator is in a form compatible with OPCODE_SWZ. + * This means that each of the operands is either an immediate value of -1, + * 0, or 1, or is a component from one source register (possibly with + * negation). + */ + uint8_t components[4] = { 0 }; + bool negate[4] = { false }; + ir_variable *var = NULL; + + for (unsigned i = 0; i < ir->type->vector_elements; i++) { + ir_rvalue *op = ir->operands[i]; + + assert(op->type->is_scalar()); + + while (op != NULL) { + switch (op->ir_type) { + case ir_type_constant: { + + assert(op->type->is_scalar()); + + const ir_constant *const c = op->as_constant(); + if (c->is_one()) { + components[i] = SWIZZLE_ONE; + } else if (c->is_zero()) { + components[i] = SWIZZLE_ZERO; + } else if (c->is_negative_one()) { + components[i] = SWIZZLE_ONE; + negate[i] = true; + } else { + assert(!"SWZ constant must be 0.0 or 1.0."); + } + + op = NULL; + break; + } + + case ir_type_dereference_variable: { + ir_dereference_variable *const deref = + (ir_dereference_variable *) op; + + assert((var == NULL) || (deref->var == var)); + components[i] = SWIZZLE_X; + var = deref->var; + op = NULL; + break; + } + + case ir_type_expression: { + ir_expression *const expr = (ir_expression *) op; + + assert(expr->operation == ir_unop_neg); + negate[i] = true; + + op = expr->operands[0]; + break; + } + + case ir_type_swizzle: { + ir_swizzle *const swiz = (ir_swizzle *) op; + + components[i] = swiz->mask.x; + op = swiz->val; + break; + } + + default: + assert(!"Should not get here."); + return; + } + } + } + + assert(var != NULL); + + ir_dereference_variable *const deref = + new(mem_ctx) ir_dereference_variable(var); + + this->result.file = PROGRAM_UNDEFINED; + deref->accept(this); + if (this->result.file == PROGRAM_UNDEFINED) { + ir_print_visitor v; + printf("Failed to get tree for expression operand:\n"); + deref->accept(&v); + exit(1); + } + + ir_to_mesa_src_reg src; + + src = this->result; + src.swizzle = MAKE_SWIZZLE4(components[0], + components[1], + components[2], + components[3]); + src.negate = ((unsigned(negate[0]) << 0) + | (unsigned(negate[1]) << 1) + | (unsigned(negate[2]) << 2) + | (unsigned(negate[3]) << 3)); + + /* Storage for our result. Ideally for an assignment we'd be using the + * actual storage for the result here, instead. + */ + const ir_to_mesa_src_reg result_src = get_temp(ir->type); + ir_to_mesa_dst_reg result_dst = ir_to_mesa_dst_reg_from_src(result_src); + + /* Limit writes to the channels that will be used by result_src later. + * This does limit this temp's use as a temporary for multi-instruction + * sequences. + */ + result_dst.writemask = (1 << ir->type->vector_elements) - 1; + + ir_to_mesa_emit_op1(ir, OPCODE_SWZ, result_dst, src); + this->result = result_src; +} + +void +ir_to_mesa_visitor::visit(ir_expression *ir) +{ + unsigned int operand; + struct ir_to_mesa_src_reg op[Elements(ir->operands)]; + struct ir_to_mesa_src_reg result_src; + struct ir_to_mesa_dst_reg result_dst; + + /* Quick peephole: Emit OPCODE_MAD(a, b, c) instead of ADD(MUL(a, b), c) + */ + if (ir->operation == ir_binop_add) { + if (try_emit_mad(ir, 1)) + return; + if (try_emit_mad(ir, 0)) + return; + } + if (try_emit_sat(ir)) + return; + + if (ir->operation == ir_quadop_vector) { + this->emit_swz(ir); + return; + } + + for (operand = 0; operand < ir->get_num_operands(); operand++) { + this->result.file = PROGRAM_UNDEFINED; + ir->operands[operand]->accept(this); + if (this->result.file == PROGRAM_UNDEFINED) { + ir_print_visitor v; + printf("Failed to get tree for expression operand:\n"); + ir->operands[operand]->accept(&v); + exit(1); + } + op[operand] = this->result; + + /* Matrix expression operands should have been broken down to vector + * operations already. + */ + 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 + * the actual storage for the result here, instead. + */ + result_src = get_temp(ir->type); + /* convenience for the emit functions below. */ + result_dst = ir_to_mesa_dst_reg_from_src(result_src); + /* Limit writes to the channels that will be used by result_src later. + * This does limit this temp's use as a temporary for multi-instruction + * sequences. + */ + result_dst.writemask = (1 << ir->type->vector_elements) - 1; + + switch (ir->operation) { + case ir_unop_logic_not: + ir_to_mesa_emit_op2(ir, OPCODE_SEQ, result_dst, + op[0], src_reg_for_float(0.0)); + break; + case ir_unop_neg: + op[0].negate = ~op[0].negate; + result_src = op[0]; + break; + case ir_unop_abs: + ir_to_mesa_emit_op1(ir, OPCODE_ABS, result_dst, op[0]); + break; + case ir_unop_sign: + ir_to_mesa_emit_op1(ir, OPCODE_SSG, result_dst, op[0]); + break; + case ir_unop_rcp: + ir_to_mesa_emit_scalar_op1(ir, OPCODE_RCP, result_dst, 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: + 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]); + break; + case ir_unop_sin: + ir_to_mesa_emit_scalar_op1(ir, OPCODE_SIN, result_dst, op[0]); + break; + case ir_unop_cos: + ir_to_mesa_emit_scalar_op1(ir, OPCODE_COS, result_dst, op[0]); + break; + case ir_unop_sin_reduced: + emit_scs(ir, OPCODE_SIN, result_dst, op[0]); + break; + case ir_unop_cos_reduced: + emit_scs(ir, OPCODE_COS, result_dst, op[0]); + break; + + case ir_unop_dFdx: + ir_to_mesa_emit_op1(ir, OPCODE_DDX, result_dst, op[0]); + break; + case ir_unop_dFdy: + 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; + case ir_binop_sub: + ir_to_mesa_emit_op2(ir, OPCODE_SUB, result_dst, op[0], op[1]); + break; + + case ir_binop_mul: + ir_to_mesa_emit_op2(ir, OPCODE_MUL, result_dst, op[0], op[1]); + break; + case ir_binop_div: + assert(!"not reached: should be handled by ir_div_to_mul_rcp"); + case ir_binop_mod: + assert(!"ir_binop_mod should have been converted to b * fract(a/b)"); + break; + + case ir_binop_less: + ir_to_mesa_emit_op2(ir, OPCODE_SLT, result_dst, op[0], op[1]); + break; + case ir_binop_greater: + ir_to_mesa_emit_op2(ir, OPCODE_SGT, result_dst, op[0], op[1]); + break; + case ir_binop_lequal: + ir_to_mesa_emit_op2(ir, OPCODE_SLE, result_dst, op[0], op[1]); + break; + case ir_binop_gequal: + ir_to_mesa_emit_op2(ir, OPCODE_SGE, result_dst, op[0], op[1]); + break; + case ir_binop_equal: + ir_to_mesa_emit_op2(ir, OPCODE_SEQ, result_dst, op[0], op[1]); + break; + 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]); + ir_to_mesa_emit_dp(ir, result_dst, temp, temp, vector_elements); + 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]); + ir_to_mesa_emit_dp(ir, result_dst, temp, temp, vector_elements); + 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: + assert(ir->operands[0]->type->is_vector()); + ir_to_mesa_emit_dp(ir, result_dst, op[0], op[0], + ir->operands[0]->type->vector_elements); + 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. */ + ir_to_mesa_emit_op2(ir, OPCODE_ADD, + result_dst, + op[0], op[1]); + + ir_to_mesa_emit_op2(ir, OPCODE_SNE, + result_dst, + result_src, src_reg_for_float(0.0)); + break; + + case ir_binop_logic_and: + /* the bool args are stored as float 0.0 or 1.0, so "mul" gives us "and". */ + ir_to_mesa_emit_op2(ir, OPCODE_MUL, + result_dst, + op[0], op[1]); + break; + + case ir_binop_dot: + assert(ir->operands[0]->type->is_vector()); + assert(ir->operands[0]->type == ir->operands[1]->type); + ir_to_mesa_emit_dp(ir, result_dst, op[0], op[1], + ir->operands[0]->type->vector_elements); + 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_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], 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]); + break; + case ir_unop_i2f: + case ir_unop_b2f: + case ir_unop_b2i: + /* Mesa IR lacks types, ints are stored as truncated floats. */ + result_src = op[0]; + break; + case ir_unop_f2i: + ir_to_mesa_emit_op1(ir, OPCODE_TRUNC, result_dst, op[0]); + break; + case ir_unop_f2b: + case ir_unop_i2b: + ir_to_mesa_emit_op2(ir, OPCODE_SNE, result_dst, + 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]); + break; + case ir_unop_ceil: + op[0].negate = ~op[0].negate; + ir_to_mesa_emit_op1(ir, OPCODE_FLR, result_dst, op[0]); + result_src.negate = ~result_src.negate; + break; + case ir_unop_floor: + ir_to_mesa_emit_op1(ir, OPCODE_FLR, result_dst, op[0]); + break; + case ir_unop_fract: + ir_to_mesa_emit_op1(ir, OPCODE_FRC, result_dst, op[0]); + break; + + case ir_binop_min: + ir_to_mesa_emit_op2(ir, OPCODE_MIN, result_dst, op[0], op[1]); + break; + case ir_binop_max: + ir_to_mesa_emit_op2(ir, OPCODE_MAX, result_dst, op[0], op[1]); + break; + case ir_binop_pow: + ir_to_mesa_emit_scalar_op2(ir, OPCODE_POW, result_dst, op[0], op[1]); + break; + + case ir_unop_bit_not: + case ir_unop_u2f: + case ir_binop_lshift: + case ir_binop_rshift: + case ir_binop_bit_and: + case ir_binop_bit_xor: + case ir_binop_bit_or: + case ir_unop_round_even: + assert(!"GLSL 1.30 features unsupported"); + break; + + case ir_quadop_vector: + /* This operation should have already been handled. + */ + assert(!"Should not get here."); + break; + } + + this->result = result_src; +} + + +void +ir_to_mesa_visitor::visit(ir_swizzle *ir) +{ + ir_to_mesa_src_reg src_reg; + int i; + int swizzle[4]; + + /* Note that this is only swizzles in expressions, not those on the left + * hand side of an assignment, which do write masking. See ir_assignment + * for that. + */ + + ir->val->accept(this); + src_reg = this->result; + assert(src_reg.file != PROGRAM_UNDEFINED); + + for (i = 0; i < 4; i++) { + if (i < ir->type->vector_elements) { + switch (i) { + case 0: + swizzle[i] = GET_SWZ(src_reg.swizzle, ir->mask.x); + break; + case 1: + swizzle[i] = GET_SWZ(src_reg.swizzle, ir->mask.y); + break; + case 2: + swizzle[i] = GET_SWZ(src_reg.swizzle, ir->mask.z); + break; + case 3: + swizzle[i] = GET_SWZ(src_reg.swizzle, ir->mask.w); + break; + } + } else { + /* If the type is smaller than a vec4, replicate the last + * channel out. + */ + swizzle[i] = swizzle[ir->type->vector_elements - 1]; + } + } + + src_reg.swizzle = MAKE_SWIZZLE4(swizzle[0], + swizzle[1], + swizzle[2], + swizzle[3]); + + this->result = src_reg; +} + +void +ir_to_mesa_visitor::visit(ir_dereference_variable *ir) +{ + variable_storage *entry = find_variable_storage(ir->var); + ir_variable *var = ir->var; + + if (!entry) { + switch (var->mode) { + case ir_var_uniform: + entry = new(mem_ctx) variable_storage(var, PROGRAM_UNIFORM, + var->location); + this->variables.push_tail(entry); + break; + case ir_var_in: + case ir_var_inout: + /* The linker assigns locations for varyings and attributes, + * including deprecated builtins (like gl_Color), user-assign + * generic attributes (glBindVertexLocation), and + * user-defined varyings. + * + * FINISHME: We would hit this path for function arguments. Fix! + */ + assert(var->location != -1); + entry = new(mem_ctx) variable_storage(var, + PROGRAM_INPUT, + var->location); + if (this->prog->Target == GL_VERTEX_PROGRAM_ARB && + var->location >= VERT_ATTRIB_GENERIC0) { + _mesa_add_attribute(this->prog->Attributes, + var->name, + _mesa_sizeof_glsl_type(var->type->gl_type), + var->type->gl_type, + var->location - VERT_ATTRIB_GENERIC0); + } + break; + case ir_var_out: + assert(var->location != -1); + entry = new(mem_ctx) variable_storage(var, + PROGRAM_OUTPUT, + var->location); + break; + case ir_var_system_value: + entry = new(mem_ctx) variable_storage(var, + PROGRAM_SYSTEM_VALUE, + var->location); + break; + case ir_var_auto: + case ir_var_temporary: + entry = new(mem_ctx) variable_storage(var, PROGRAM_TEMPORARY, + this->next_temp); + this->variables.push_tail(entry); + + next_temp += type_size(var->type); + break; + } + + if (!entry) { + printf("Failed to make storage for %s\n", var->name); + exit(1); + } + } + + this->result = ir_to_mesa_src_reg(entry->file, entry->index, var->type); +} + +void +ir_to_mesa_visitor::visit(ir_dereference_array *ir) +{ + ir_constant *index; + ir_to_mesa_src_reg src_reg; + int element_size = type_size(ir->type); + + index = ir->array_index->constant_expression_value(); + + ir->array->accept(this); + src_reg = this->result; + + if (index) { + src_reg.index += index->value.i[0] * element_size; + } else { + ir_to_mesa_src_reg array_base = this->result; + /* Variable index array dereference. It eats the "vec4" of the + * base of the array and an index that offsets the Mesa register + * index. + */ + ir->array_index->accept(this); + + ir_to_mesa_src_reg index_reg; + + if (element_size == 1) { + index_reg = this->result; + } else { + index_reg = get_temp(glsl_type::float_type); + + 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 = ralloc(mem_ctx, ir_to_mesa_src_reg); + memcpy(src_reg.reladdr, &index_reg, sizeof(index_reg)); + } + + /* If the type is smaller than a vec4, replicate the last channel out. */ + if (ir->type->is_scalar() || ir->type->is_vector()) + src_reg.swizzle = swizzle_for_size(ir->type->vector_elements); + else + src_reg.swizzle = SWIZZLE_NOOP; + + this->result = src_reg; +} + +void +ir_to_mesa_visitor::visit(ir_dereference_record *ir) +{ + unsigned int i; + const glsl_type *struct_type = ir->record->type; + int offset = 0; + + ir->record->accept(this); + + for (i = 0; i < struct_type->length; i++) { + if (strcmp(struct_type->fields.structure[i].name, ir->field) == 0) + break; + offset += type_size(struct_type->fields.structure[i].type); + } + + /* If the type is smaller than a vec4, replicate the last channel out. */ + if (ir->type->is_scalar() || ir->type->is_vector()) + this->result.swizzle = swizzle_for_size(ir->type->vector_elements); + else + this->result.swizzle = SWIZZLE_NOOP; + + this->result.index += offset; +} + +/** + * 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. + */ +static struct ir_to_mesa_dst_reg +get_assignment_lhs(ir_dereference *ir, ir_to_mesa_visitor *v) +{ + /* 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(); + if (deref_array) { + assert(!deref_array->array->type->is_vector()); + } + + /* Use the rvalue deref handler for the most part. We'll ignore + * swizzles in it and write swizzles using writemask, though. + */ + ir->accept(v); + return ir_to_mesa_dst_reg_from_src(v->result); +} + +/** + * Process the condition of a conditional assignment + * + * Examines the condition of a conditional assignment to generate the optimal + * first operand of a \c CMP instruction. If the condition is a relational + * operator with 0 (e.g., \c ir_binop_less), the value being compared will be + * used as the source for the \c CMP instruction. Otherwise the comparison + * is processed to a boolean result, and the boolean result is used as the + * operand to the CMP instruction. + */ +bool +ir_to_mesa_visitor::process_move_condition(ir_rvalue *ir) +{ + ir_rvalue *src_ir = ir; + bool negate = true; + bool switch_order = false; + + ir_expression *const expr = ir->as_expression(); + if ((expr != NULL) && (expr->get_num_operands() == 2)) { + bool zero_on_left = false; + + if (expr->operands[0]->is_zero()) { + src_ir = expr->operands[1]; + zero_on_left = true; + } else if (expr->operands[1]->is_zero()) { + src_ir = expr->operands[0]; + zero_on_left = false; + } + + /* a is - 0 + - 0 + + * (a < 0) T F F ( a < 0) T F F + * (0 < a) F F T (-a < 0) F F T + * (a <= 0) T T F (-a < 0) F F T (swap order of other operands) + * (0 <= a) F T T ( a < 0) T F F (swap order of other operands) + * (a > 0) F F T (-a < 0) F F T + * (0 > a) T F F ( a < 0) T F F + * (a >= 0) F T T ( a < 0) T F F (swap order of other operands) + * (0 >= a) T T F (-a < 0) F F T (swap order of other operands) + * + * Note that exchanging the order of 0 and 'a' in the comparison simply + * means that the value of 'a' should be negated. + */ + if (src_ir != ir) { + switch (expr->operation) { + case ir_binop_less: + switch_order = false; + negate = zero_on_left; + break; + + case ir_binop_greater: + switch_order = false; + negate = !zero_on_left; + break; + + case ir_binop_lequal: + switch_order = true; + negate = !zero_on_left; + break; + + case ir_binop_gequal: + switch_order = true; + negate = zero_on_left; + break; + + default: + /* This isn't the right kind of comparison afterall, so make sure + * the whole condition is visited. + */ + src_ir = ir; + break; + } + } + } + + src_ir->accept(this); + + /* We use the OPCODE_CMP (a < 0 ? b : c) for conditional moves, and the + * condition we produced is 0.0 or 1.0. By flipping the sign, we can + * choose which value OPCODE_CMP produces without an extra instruction + * computing the condition. + */ + if (negate) + this->result.negate = ~this->result.negate; + + return switch_order; +} + +void +ir_to_mesa_visitor::visit(ir_assignment *ir) +{ + struct ir_to_mesa_dst_reg l; + struct ir_to_mesa_src_reg r; + int i; + + ir->rhs->accept(this); + r = this->result; + + 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); + + if (ir->condition) { + const bool switch_order = this->process_move_condition(ir->condition); + ir_to_mesa_src_reg condition = this->result; + + for (i = 0; i < type_size(ir->lhs->type); i++) { + if (switch_order) { + ir_to_mesa_emit_op3(ir, OPCODE_CMP, l, + condition, ir_to_mesa_src_reg_from_dst(l), r); + } else { + ir_to_mesa_emit_op3(ir, OPCODE_CMP, l, + condition, r, ir_to_mesa_src_reg_from_dst(l)); + } + + l.index++; + r.index++; + } + } else { + for (i = 0; i < type_size(ir->lhs->type); i++) { + ir_to_mesa_emit_op1(ir, OPCODE_MOV, l, r); + l.index++; + r.index++; + } + } +} + + +void +ir_to_mesa_visitor::visit(ir_constant *ir) +{ + ir_to_mesa_src_reg src_reg; + GLfloat stack_vals[4] = { 0 }; + GLfloat *values = stack_vals; + unsigned int i; + + /* Unfortunately, 4 floats is all we can get into + * _mesa_add_unnamed_constant. So, make a temp to store an + * aggregate constant and move each constant value into it. If we + * get lucky, copy propagation will eliminate the extra moves. + */ + + if (ir->type->base_type == GLSL_TYPE_STRUCT) { + ir_to_mesa_src_reg temp_base = get_temp(ir->type); + ir_to_mesa_dst_reg temp = ir_to_mesa_dst_reg_from_src(temp_base); + + foreach_iter(exec_list_iterator, iter, ir->components) { + ir_constant *field_value = (ir_constant *)iter.get(); + int size = type_size(field_value->type); + + assert(size > 0); + + field_value->accept(this); + src_reg = this->result; + + for (i = 0; i < (unsigned int)size; i++) { + ir_to_mesa_emit_op1(ir, OPCODE_MOV, temp, src_reg); + + src_reg.index++; + temp.index++; + } + } + this->result = temp_base; + return; + } + + if (ir->type->is_array()) { + ir_to_mesa_src_reg temp_base = get_temp(ir->type); + ir_to_mesa_dst_reg temp = ir_to_mesa_dst_reg_from_src(temp_base); + int size = type_size(ir->type->fields.array); + + assert(size > 0); + + for (i = 0; i < ir->type->length; i++) { + ir->array_elements[i]->accept(this); + src_reg = this->result; + for (int j = 0; j < size; j++) { + ir_to_mesa_emit_op1(ir, OPCODE_MOV, temp, src_reg); + + src_reg.index++; + temp.index++; + } + } + this->result = temp_base; + return; + } + + if (ir->type->is_matrix()) { + ir_to_mesa_src_reg mat = get_temp(ir->type); + ir_to_mesa_dst_reg mat_column = ir_to_mesa_dst_reg_from_src(mat); + + for (i = 0; i < ir->type->matrix_columns; i++) { + assert(ir->type->base_type == GLSL_TYPE_FLOAT); + values = &ir->value.f[i * ir->type->vector_elements]; + + src_reg = ir_to_mesa_src_reg(PROGRAM_CONSTANT, -1, NULL); + src_reg.index = _mesa_add_unnamed_constant(this->prog->Parameters, + values, + ir->type->vector_elements, + &src_reg.swizzle); + ir_to_mesa_emit_op1(ir, OPCODE_MOV, mat_column, src_reg); + + mat_column.index++; + } + + this->result = mat; + return; + } + + src_reg.file = PROGRAM_CONSTANT; + switch (ir->type->base_type) { + case GLSL_TYPE_FLOAT: + values = &ir->value.f[0]; + break; + case GLSL_TYPE_UINT: + for (i = 0; i < ir->type->vector_elements; i++) { + values[i] = ir->value.u[i]; + } + break; + case GLSL_TYPE_INT: + for (i = 0; i < ir->type->vector_elements; i++) { + values[i] = ir->value.i[i]; + } + break; + case GLSL_TYPE_BOOL: + for (i = 0; i < ir->type->vector_elements; i++) { + values[i] = ir->value.b[i]; + } + break; + default: + assert(!"Non-float/uint/int/bool constant"); + } + + this->result = ir_to_mesa_src_reg(PROGRAM_CONSTANT, -1, ir->type); + this->result.index = _mesa_add_unnamed_constant(this->prog->Parameters, + values, + ir->type->vector_elements, + &this->result.swizzle); +} + +function_entry * +ir_to_mesa_visitor::get_function_signature(ir_function_signature *sig) +{ + function_entry *entry; + + foreach_iter(exec_list_iterator, iter, this->function_signatures) { + entry = (function_entry *)iter.get(); + + if (entry->sig == sig) + return entry; + } + + entry = ralloc(mem_ctx, function_entry); + entry->sig = sig; + entry->sig_id = this->next_signature_id++; + entry->bgn_inst = NULL; + + /* Allocate storage for all the parameters. */ + foreach_iter(exec_list_iterator, iter, sig->parameters) { + ir_variable *param = (ir_variable *)iter.get(); + variable_storage *storage; + + storage = find_variable_storage(param); + assert(!storage); + + storage = new(mem_ctx) variable_storage(param, PROGRAM_TEMPORARY, + this->next_temp); + this->variables.push_tail(storage); + + this->next_temp += type_size(param->type); + } + + if (!sig->return_type->is_void()) { + entry->return_reg = get_temp(sig->return_type); + } else { + entry->return_reg = ir_to_mesa_undef; + } + + this->function_signatures.push_tail(entry); + return entry; +} + +void +ir_to_mesa_visitor::visit(ir_call *ir) +{ + ir_to_mesa_instruction *call_inst; + ir_function_signature *sig = ir->get_callee(); + function_entry *entry = get_function_signature(sig); + int i; + + /* Process in parameters. */ + exec_list_iterator sig_iter = sig->parameters.iterator(); + foreach_iter(exec_list_iterator, iter, *ir) { + ir_rvalue *param_rval = (ir_rvalue *)iter.get(); + ir_variable *param = (ir_variable *)sig_iter.get(); + + if (param->mode == ir_var_in || + param->mode == ir_var_inout) { + variable_storage *storage = find_variable_storage(param); + assert(storage); + + param_rval->accept(this); + ir_to_mesa_src_reg r = this->result; + + ir_to_mesa_dst_reg l; + l.file = storage->file; + l.index = storage->index; + l.reladdr = NULL; + l.writemask = WRITEMASK_XYZW; + l.cond_mask = COND_TR; + + for (i = 0; i < type_size(param->type); i++) { + ir_to_mesa_emit_op1(ir, OPCODE_MOV, l, r); + l.index++; + r.index++; + } + } + + sig_iter.next(); + } + assert(!sig_iter.has_next()); + + /* Emit call instruction */ + call_inst = ir_to_mesa_emit_op1(ir, OPCODE_CAL, + ir_to_mesa_undef_dst, ir_to_mesa_undef); + call_inst->function = entry; + + /* Process out parameters. */ + sig_iter = sig->parameters.iterator(); + foreach_iter(exec_list_iterator, iter, *ir) { + ir_rvalue *param_rval = (ir_rvalue *)iter.get(); + ir_variable *param = (ir_variable *)sig_iter.get(); + + if (param->mode == ir_var_out || + param->mode == ir_var_inout) { + variable_storage *storage = find_variable_storage(param); + assert(storage); + + ir_to_mesa_src_reg r; + r.file = storage->file; + r.index = storage->index; + r.reladdr = NULL; + r.swizzle = SWIZZLE_NOOP; + r.negate = 0; + + param_rval->accept(this); + ir_to_mesa_dst_reg l = ir_to_mesa_dst_reg_from_src(this->result); + + for (i = 0; i < type_size(param->type); i++) { + ir_to_mesa_emit_op1(ir, OPCODE_MOV, l, r); + l.index++; + r.index++; + } + } + + sig_iter.next(); + } + assert(!sig_iter.has_next()); + + /* Process return value. */ + this->result = entry->return_reg; +} + +void +ir_to_mesa_visitor::visit(ir_texture *ir) +{ + ir_to_mesa_src_reg result_src, coord, lod_info, projector; + ir_to_mesa_dst_reg result_dst, coord_dst; + ir_to_mesa_instruction *inst = NULL; + prog_opcode opcode = OPCODE_NOP; + + ir->coordinate->accept(this); + + /* Put our coords in a temp. We'll need to modify them for shadow, + * projection, or LOD, so the only case we'd use it as is is if + * we're doing plain old texturing. Mesa IR optimization should + * handle cleaning up our mess in that case. + */ + coord = get_temp(glsl_type::vec4_type); + coord_dst = ir_to_mesa_dst_reg_from_src(coord); + ir_to_mesa_emit_op1(ir, OPCODE_MOV, coord_dst, + this->result); + + if (ir->projector) { + ir->projector->accept(this); + projector = this->result; + } + + /* Storage for our result. Ideally for an assignment we'd be using + * the actual storage for the result here, instead. + */ + result_src = get_temp(glsl_type::vec4_type); + result_dst = ir_to_mesa_dst_reg_from_src(result_src); + + switch (ir->op) { + case ir_tex: + opcode = OPCODE_TEX; + break; + case ir_txb: + opcode = OPCODE_TXB; + ir->lod_info.bias->accept(this); + lod_info = this->result; + break; + case ir_txl: + opcode = OPCODE_TXL; + ir->lod_info.lod->accept(this); + lod_info = this->result; + break; + case ir_txd: + case ir_txf: + assert(!"GLSL 1.30 features unsupported"); + break; + } + + if (ir->projector) { + if (opcode == OPCODE_TEX) { + /* Slot the projector in as the last component of the coord. */ + coord_dst.writemask = WRITEMASK_W; + ir_to_mesa_emit_op1(ir, OPCODE_MOV, coord_dst, projector); + coord_dst.writemask = WRITEMASK_XYZW; + opcode = OPCODE_TXP; + } else { + ir_to_mesa_src_reg coord_w = coord; + coord_w.swizzle = SWIZZLE_WWWW; + + /* For the other TEX opcodes there's no projective version + * since the last slot is taken up by lod info. Do the + * projective divide now. + */ + coord_dst.writemask = WRITEMASK_W; + ir_to_mesa_emit_op1(ir, OPCODE_RCP, coord_dst, projector); + + coord_dst.writemask = WRITEMASK_XYZ; + ir_to_mesa_emit_op2(ir, OPCODE_MUL, coord_dst, coord, coord_w); + + coord_dst.writemask = WRITEMASK_XYZW; + coord.swizzle = SWIZZLE_XYZW; + } + } + + if (ir->shadow_comparitor) { + /* Slot the shadow value in as the second to last component of the + * coord. + */ + ir->shadow_comparitor->accept(this); + coord_dst.writemask = WRITEMASK_Z; + ir_to_mesa_emit_op1(ir, OPCODE_MOV, coord_dst, this->result); + coord_dst.writemask = WRITEMASK_XYZW; + } + + if (opcode == OPCODE_TXL || opcode == OPCODE_TXB) { + /* Mesa IR stores lod or lod bias in the last channel of the coords. */ + coord_dst.writemask = WRITEMASK_W; + ir_to_mesa_emit_op1(ir, OPCODE_MOV, coord_dst, lod_info); + coord_dst.writemask = WRITEMASK_XYZW; + } + + inst = ir_to_mesa_emit_op1(ir, opcode, result_dst, coord); + + if (ir->shadow_comparitor) + inst->tex_shadow = GL_TRUE; + + inst->sampler = _mesa_get_sampler_uniform_value(ir->sampler, + this->shader_program, + this->prog); + + const glsl_type *sampler_type = ir->sampler->type; + + switch (sampler_type->sampler_dimensionality) { + case GLSL_SAMPLER_DIM_1D: + inst->tex_target = (sampler_type->sampler_array) + ? TEXTURE_1D_ARRAY_INDEX : TEXTURE_1D_INDEX; + break; + case GLSL_SAMPLER_DIM_2D: + 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; + break; + 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(!"Should not get here."); + } + + this->result = result_src; +} + +void +ir_to_mesa_visitor::visit(ir_return *ir) +{ + 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; + + l = ir_to_mesa_dst_reg_from_src(current_function->return_reg); + + for (i = 0; i < type_size(current_function->sig->return_type); i++) { + ir_to_mesa_emit_op1(ir, OPCODE_MOV, l, r); + l.index++; + r.index++; + } + } + + ir_to_mesa_emit_op0(ir, OPCODE_RET); +} + +void +ir_to_mesa_visitor::visit(ir_discard *ir) +{ + struct gl_fragment_program *fp = (struct gl_fragment_program *)this->prog; + + if (ir->condition) { + ir->condition->accept(this); + this->result.negate = ~this->result.negate; + ir_to_mesa_emit_op1(ir, OPCODE_KIL, ir_to_mesa_undef_dst, this->result); + } else { + ir_to_mesa_emit_op0(ir, OPCODE_KIL_NV); + } + + fp->UsesKill = GL_TRUE; +} + +void +ir_to_mesa_visitor::visit(ir_if *ir) +{ + ir_to_mesa_instruction *cond_inst, *if_inst, *else_inst = NULL; + ir_to_mesa_instruction *prev_inst; + + prev_inst = (ir_to_mesa_instruction *)this->instructions.get_tail(); + + ir->condition->accept(this); + assert(this->result.file != PROGRAM_UNDEFINED); + + if (this->options->EmitCondCodes) { + cond_inst = (ir_to_mesa_instruction *)this->instructions.get_tail(); + + /* See if we actually generated any instruction for generating + * the condition. If not, then cook up a move to a temp so we + * have something to set cond_update on. + */ + if (cond_inst == prev_inst) { + ir_to_mesa_src_reg temp = get_temp(glsl_type::bool_type); + cond_inst = ir_to_mesa_emit_op1(ir->condition, OPCODE_MOV, + ir_to_mesa_dst_reg_from_src(temp), + result); + } + cond_inst->cond_update = GL_TRUE; + + if_inst = ir_to_mesa_emit_op0(ir->condition, OPCODE_IF); + if_inst->dst_reg.cond_mask = COND_NE; + } else { + if_inst = ir_to_mesa_emit_op1(ir->condition, + OPCODE_IF, ir_to_mesa_undef_dst, + this->result); + } + + this->instructions.push_tail(if_inst); + + visit_exec_list(&ir->then_instructions, this); + + if (!ir->else_instructions.is_empty()) { + else_inst = ir_to_mesa_emit_op0(ir->condition, OPCODE_ELSE); + visit_exec_list(&ir->else_instructions, this); + } + + if_inst = ir_to_mesa_emit_op1(ir->condition, OPCODE_ENDIF, + ir_to_mesa_undef_dst, ir_to_mesa_undef); +} + +ir_to_mesa_visitor::ir_to_mesa_visitor() +{ + result.file = PROGRAM_UNDEFINED; + next_temp = 1; + next_signature_id = 1; + current_function = NULL; + mem_ctx = ralloc_context(NULL); +} + +ir_to_mesa_visitor::~ir_to_mesa_visitor() +{ + ralloc_free(mem_ctx); +} + +static struct prog_src_register +mesa_src_reg_from_ir_src_reg(ir_to_mesa_src_reg reg) +{ + struct prog_src_register mesa_reg; + + mesa_reg.File = reg.file; + assert(reg.index < (1 << INST_INDEX_BITS)); + mesa_reg.Index = reg.index; + mesa_reg.Swizzle = reg.swizzle; + mesa_reg.RelAddr = reg.reladdr != NULL; + 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; +} + +static void +set_branchtargets(ir_to_mesa_visitor *v, + struct prog_instruction *mesa_instructions, + int num_instructions) +{ + int if_count = 0, loop_count = 0; + int *if_stack, *loop_stack; + int if_stack_pos = 0, loop_stack_pos = 0; + int i, j; + + for (i = 0; i < num_instructions; i++) { + switch (mesa_instructions[i].Opcode) { + case OPCODE_IF: + if_count++; + break; + case OPCODE_BGNLOOP: + loop_count++; + break; + case OPCODE_BRK: + case OPCODE_CONT: + mesa_instructions[i].BranchTarget = -1; + break; + default: + break; + } + } + + if_stack = rzalloc_array(v->mem_ctx, int, if_count); + loop_stack = rzalloc_array(v->mem_ctx, int, loop_count); + + for (i = 0; i < num_instructions; i++) { + switch (mesa_instructions[i].Opcode) { + case OPCODE_IF: + if_stack[if_stack_pos] = i; + if_stack_pos++; + break; + case OPCODE_ELSE: + mesa_instructions[if_stack[if_stack_pos - 1]].BranchTarget = i; + if_stack[if_stack_pos - 1] = i; + break; + case OPCODE_ENDIF: + mesa_instructions[if_stack[if_stack_pos - 1]].BranchTarget = i; + if_stack_pos--; + break; + case OPCODE_BGNLOOP: + loop_stack[loop_stack_pos] = i; + loop_stack_pos++; + break; + case OPCODE_ENDLOOP: + loop_stack_pos--; + /* Rewrite any breaks/conts at this nesting level (haven't + * already had a BranchTarget assigned) to point to the end + * of the loop. + */ + for (j = loop_stack[loop_stack_pos]; j < i; j++) { + if (mesa_instructions[j].Opcode == OPCODE_BRK || + mesa_instructions[j].Opcode == OPCODE_CONT) { + if (mesa_instructions[j].BranchTarget == -1) { + mesa_instructions[j].BranchTarget = i; + } + } + } + /* The loop ends point at each other. */ + mesa_instructions[i].BranchTarget = loop_stack[loop_stack_pos]; + mesa_instructions[loop_stack[loop_stack_pos]].BranchTarget = i; + break; + case OPCODE_CAL: + foreach_iter(exec_list_iterator, iter, v->function_signatures) { + function_entry *entry = (function_entry *)iter.get(); + + if (entry->sig_id == mesa_instructions[i].BranchTarget) { + mesa_instructions[i].BranchTarget = entry->inst; + break; + } + } + break; + default: + break; + } + } +} + +static void +print_program(struct prog_instruction *mesa_instructions, + ir_instruction **mesa_instruction_annotation, + int num_instructions) +{ + ir_instruction *last_ir = NULL; + int i; + int indent = 0; + + for (i = 0; i < num_instructions; i++) { + struct prog_instruction *mesa_inst = mesa_instructions + i; + ir_instruction *ir = mesa_instruction_annotation[i]; + + fprintf(stdout, "%3d: ", i); + + if (last_ir != ir && ir) { + int j; + + for (j = 0; j < indent; j++) { + fprintf(stdout, " "); + } + ir->print(); + printf("\n"); + last_ir = ir; + + fprintf(stdout, " "); /* line number spacing. */ + } + + indent = _mesa_fprint_instruction_opt(stdout, mesa_inst, indent, + PROG_PRINT_DEBUG, NULL); + } +} + + +/** + * Count resources used by the given gpu program (number of texture + * samplers, etc). + */ +static void +count_resources(struct gl_program *prog) +{ + unsigned int i; + + prog->SamplersUsed = 0; + + for (i = 0; i < prog->NumInstructions; i++) { + struct prog_instruction *inst = &prog->Instructions[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; + } + } + } + + _mesa_update_shader_textures_used(prog); +} + + +/** + * Check if the given vertex/fragment/shader program is within the + * resource limits of the context (number of texture units, etc). + * If any of those checks fail, record a linker error. + * + * XXX more checks are needed... + */ +static void +check_resources(const struct gl_context *ctx, + struct gl_shader_program *shader_program, + struct gl_program *prog) +{ + switch (prog->Target) { + case GL_VERTEX_PROGRAM_ARB: + if (_mesa_bitcount(prog->SamplersUsed) > + ctx->Const.MaxVertexTextureImageUnits) { + fail_link(shader_program, "Too many vertex shader texture samplers"); + } + if (prog->Parameters->NumParameters > MAX_UNIFORMS) { + fail_link(shader_program, "Too many vertex shader constants"); + } + break; + case MESA_GEOMETRY_PROGRAM: + if (_mesa_bitcount(prog->SamplersUsed) > + ctx->Const.MaxGeometryTextureImageUnits) { + fail_link(shader_program, "Too many geometry shader texture samplers"); + } + if (prog->Parameters->NumParameters > + MAX_GEOMETRY_UNIFORM_COMPONENTS / 4) { + fail_link(shader_program, "Too many geometry shader constants"); + } + break; + case GL_FRAGMENT_PROGRAM_ARB: + if (_mesa_bitcount(prog->SamplersUsed) > + ctx->Const.MaxTextureImageUnits) { + fail_link(shader_program, "Too many fragment shader texture samplers"); + } + if (prog->Parameters->NumParameters > MAX_UNIFORMS) { + fail_link(shader_program, "Too many fragment shader constants"); + } + break; + default: + _mesa_problem(ctx, "unexpected program type in check_resources()"); + } +} + + + +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) +{ + struct uniform_sort *u1 = (struct uniform_sort *)a; + struct uniform_sort *u2 = (struct uniform_sort *)b; + + 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 +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; + + sorted_uniforms = ralloc_array(NULL, struct uniform_sort, + shader_program->Uniforms->NumUniforms); + + for (i = 0; i < shader_program->Uniforms->NumUniforms; i++) { + struct gl_uniform *uniform = shader_program->Uniforms->Uniforms + i; + int parameter_index = -1; + + switch (shader->Type) { + case GL_VERTEX_SHADER: + parameter_index = uniform->VertPos; + break; + case GL_FRAGMENT_SHADER: + parameter_index = uniform->FragPos; + break; + case GL_GEOMETRY_SHADER: + parameter_index = uniform->GeomPos; + 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++; + } + } + + 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); + } + } + } + + ralloc_free(sorted_uniforms); +} + +static void +set_uniform_initializer(struct gl_context *ctx, void *mem_ctx, + struct gl_shader_program *shader_program, + const char *name, const glsl_type *type, + ir_constant *val) +{ + if (type->is_record()) { + ir_constant *field_constant; + + field_constant = (ir_constant *)val->components.get_head(); + + for (unsigned int i = 0; i < type->length; i++) { + const glsl_type *field_type = type->fields.structure[i].type; + const char *field_name = ralloc_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 = ralloc_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(struct gl_context *ctx, + struct gl_shader_program *shader_program) +{ + void *mem_ctx = NULL; + + for (unsigned int i = 0; i < MESA_SHADER_TYPES; i++) { + struct gl_shader *shader = shader_program->_LinkedShaders[i]; + + if (shader == NULL) + continue; + + 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 = ralloc_context(NULL); + + set_uniform_initializer(ctx, mem_ctx, shader_program, var->name, + var->type, var->constant_value); + } + } + + ralloc_free(mem_ctx); +} + +/* + * On a basic block basis, tracks available PROGRAM_TEMPORARY register + * channels for copy propagation and updates following instructions to + * use the original versions. + * + * The ir_to_mesa_visitor lazily produces code assuming that this pass + * will occur. As an example, a TXP production before this pass: + * + * 0: MOV TEMP[1], INPUT[4].xyyy; + * 1: MOV TEMP[1].w, INPUT[4].wwww; + * 2: TXP TEMP[2], TEMP[1], texture[0], 2D; + * + * and after: + * + * 0: MOV TEMP[1], INPUT[4].xyyy; + * 1: MOV TEMP[1].w, INPUT[4].wwww; + * 2: TXP TEMP[2], INPUT[4].xyyw, texture[0], 2D; + * + * which allows for dead code elimination on TEMP[1]'s writes. + */ +void +ir_to_mesa_visitor::copy_propagate(void) +{ + ir_to_mesa_instruction **acp = rzalloc_array(mem_ctx, + ir_to_mesa_instruction *, + this->next_temp * 4); + int *acp_level = rzalloc_array(mem_ctx, int, this->next_temp * 4); + int level = 0; + + foreach_iter(exec_list_iterator, iter, this->instructions) { + ir_to_mesa_instruction *inst = (ir_to_mesa_instruction *)iter.get(); + + assert(inst->dst_reg.file != PROGRAM_TEMPORARY + || inst->dst_reg.index < this->next_temp); + + /* First, do any copy propagation possible into the src regs. */ + for (int r = 0; r < 3; r++) { + ir_to_mesa_instruction *first = NULL; + bool good = true; + int acp_base = inst->src_reg[r].index * 4; + + if (inst->src_reg[r].file != PROGRAM_TEMPORARY || + inst->src_reg[r].reladdr) + continue; + + /* See if we can find entries in the ACP consisting of MOVs + * from the same src register for all the swizzled channels + * of this src register reference. + */ + for (int i = 0; i < 4; i++) { + int src_chan = GET_SWZ(inst->src_reg[r].swizzle, i); + ir_to_mesa_instruction *copy_chan = acp[acp_base + src_chan]; + + if (!copy_chan) { + good = false; + break; + } + + assert(acp_level[acp_base + src_chan] <= level); + + if (!first) { + first = copy_chan; + } else { + if (first->src_reg[0].file != copy_chan->src_reg[0].file || + first->src_reg[0].index != copy_chan->src_reg[0].index) { + good = false; + break; + } + } + } + + if (good) { + /* We've now validated that we can copy-propagate to + * replace this src register reference. Do it. + */ + inst->src_reg[r].file = first->src_reg[0].file; + inst->src_reg[r].index = first->src_reg[0].index; + + int swizzle = 0; + for (int i = 0; i < 4; i++) { + int src_chan = GET_SWZ(inst->src_reg[r].swizzle, i); + ir_to_mesa_instruction *copy_inst = acp[acp_base + src_chan]; + swizzle |= (GET_SWZ(copy_inst->src_reg[0].swizzle, src_chan) << + (3 * i)); + } + inst->src_reg[r].swizzle = swizzle; + } + } + + switch (inst->op) { + case OPCODE_BGNLOOP: + case OPCODE_ENDLOOP: + /* End of a basic block, clear the ACP entirely. */ + memset(acp, 0, sizeof(*acp) * this->next_temp * 4); + break; + + case OPCODE_IF: + ++level; + break; + + case OPCODE_ENDIF: + case OPCODE_ELSE: + /* Clear all channels written inside the block from the ACP, but + * leaving those that were not touched. + */ + for (int r = 0; r < this->next_temp; r++) { + for (int c = 0; c < 4; c++) { + if (!acp[4 * r + c]) + continue; + + if (acp_level[4 * r + c] >= level) + acp[4 * r + c] = NULL; + } + } + if (inst->op == OPCODE_ENDIF) + --level; + break; + + default: + /* Continuing the block, clear any written channels from + * the ACP. + */ + if (inst->dst_reg.file == PROGRAM_TEMPORARY && inst->dst_reg.reladdr) { + /* Any temporary might be written, so no copy propagation + * across this instruction. + */ + memset(acp, 0, sizeof(*acp) * this->next_temp * 4); + } else if (inst->dst_reg.file == PROGRAM_OUTPUT && + inst->dst_reg.reladdr) { + /* Any output might be written, so no copy propagation + * from outputs across this instruction. + */ + for (int r = 0; r < this->next_temp; r++) { + for (int c = 0; c < 4; c++) { + if (!acp[4 * r + c]) + continue; + + if (acp[4 * r + c]->src_reg[0].file == PROGRAM_OUTPUT) + acp[4 * r + c] = NULL; + } + } + } else if (inst->dst_reg.file == PROGRAM_TEMPORARY || + inst->dst_reg.file == PROGRAM_OUTPUT) { + /* Clear where it's used as dst. */ + if (inst->dst_reg.file == PROGRAM_TEMPORARY) { + for (int c = 0; c < 4; c++) { + if (inst->dst_reg.writemask & (1 << c)) { + acp[4 * inst->dst_reg.index + c] = NULL; + } + } + } + + /* Clear where it's used as src. */ + for (int r = 0; r < this->next_temp; r++) { + for (int c = 0; c < 4; c++) { + if (!acp[4 * r + c]) + continue; + + int src_chan = GET_SWZ(acp[4 * r + c]->src_reg[0].swizzle, c); + + if (acp[4 * r + c]->src_reg[0].file == inst->dst_reg.file && + acp[4 * r + c]->src_reg[0].index == inst->dst_reg.index && + inst->dst_reg.writemask & (1 << src_chan)) + { + acp[4 * r + c] = NULL; + } + } + } + } + break; + } + + /* If this is a copy, add it to the ACP. */ + if (inst->op == OPCODE_MOV && + inst->dst_reg.file == PROGRAM_TEMPORARY && + !inst->dst_reg.reladdr && + !inst->saturate && + !inst->src_reg[0].reladdr && + !inst->src_reg[0].negate) { + for (int i = 0; i < 4; i++) { + if (inst->dst_reg.writemask & (1 << i)) { + acp[4 * inst->dst_reg.index + i] = inst; + acp_level[4 * inst->dst_reg.index + i] = level; + } + } + } + } + + ralloc_free(acp_level); + ralloc_free(acp); +} + + +/** + * Convert a shader's GLSL IR into a Mesa gl_program. + */ +static struct gl_program * +get_mesa_program(struct gl_context *ctx, + struct gl_shader_program *shader_program, + struct gl_shader *shader) +{ + ir_to_mesa_visitor v; + struct prog_instruction *mesa_instructions, *mesa_inst; + ir_instruction **mesa_instruction_annotation; + int i; + struct gl_program *prog; + 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: + target = GL_VERTEX_PROGRAM_ARB; + target_string = "vertex"; + break; + case GL_FRAGMENT_SHADER: + target = GL_FRAGMENT_PROGRAM_ARB; + target_string = "fragment"; + break; + case GL_GEOMETRY_SHADER: + target = GL_GEOMETRY_PROGRAM_NV; + target_string = "geometry"; + break; + default: + assert(!"should not be reached"); + return NULL; + } + + validate_ir_tree(shader->ir); + + prog = ctx->Driver.NewProgram(ctx, target, shader_program->Name); + if (!prog) + return NULL; + prog->Parameters = _mesa_new_parameter_list(); + prog->Varying = _mesa_new_parameter_list(); + prog->Attributes = _mesa_new_parameter_list(); + v.ctx = ctx; + v.prog = prog; + v.shader_program = shader_program; + v.options = options; + + add_uniforms_to_parameters_list(shader_program, shader, prog); + + /* Emit Mesa IR for main(). */ + visit_exec_list(shader->ir, &v); + v.ir_to_mesa_emit_op0(NULL, OPCODE_END); + + /* Now emit bodies for any functions that were used. */ + do { + progress = GL_FALSE; + + foreach_iter(exec_list_iterator, iter, v.function_signatures) { + function_entry *entry = (function_entry *)iter.get(); + + if (!entry->bgn_inst) { + v.current_function = entry; + + entry->bgn_inst = v.ir_to_mesa_emit_op0(NULL, OPCODE_BGNSUB); + entry->bgn_inst->function = entry; + + visit_exec_list(&entry->sig->body, &v); + + ir_to_mesa_instruction *last; + last = (ir_to_mesa_instruction *)v.instructions.get_tail(); + if (last->op != OPCODE_RET) + v.ir_to_mesa_emit_op0(NULL, OPCODE_RET); + + ir_to_mesa_instruction *end; + end = v.ir_to_mesa_emit_op0(NULL, OPCODE_ENDSUB); + end->function = entry; + + progress = GL_TRUE; + } + } + } while (progress); + + prog->NumTemporaries = v.next_temp; + + int num_instructions = 0; + foreach_iter(exec_list_iterator, iter, v.instructions) { + num_instructions++; + } + + mesa_instructions = + (struct prog_instruction *)calloc(num_instructions, + sizeof(*mesa_instructions)); + mesa_instruction_annotation = ralloc_array(v.mem_ctx, ir_instruction *, + num_instructions); + + v.copy_propagate(); + + /* Convert ir_mesa_instructions into prog_instructions. + */ + mesa_inst = mesa_instructions; + i = 0; + foreach_iter(exec_list_iterator, iter, v.instructions) { + const ir_to_mesa_instruction *inst = (ir_to_mesa_instruction *)iter.get(); + + mesa_inst->Opcode = inst->op; + mesa_inst->CondUpdate = inst->cond_update; + if (inst->saturate) + mesa_inst->SaturateMode = SATURATE_ZERO_ONE; + mesa_inst->DstReg.File = inst->dst_reg.file; + mesa_inst->DstReg.Index = inst->dst_reg.index; + mesa_inst->DstReg.CondMask = inst->dst_reg.cond_mask; + mesa_inst->DstReg.WriteMask = inst->dst_reg.writemask; + mesa_inst->DstReg.RelAddr = inst->dst_reg.reladdr != NULL; + mesa_inst->SrcReg[0] = mesa_src_reg_from_ir_src_reg(inst->src_reg[0]); + mesa_inst->SrcReg[1] = mesa_src_reg_from_ir_src_reg(inst->src_reg[1]); + mesa_inst->SrcReg[2] = mesa_src_reg_from_ir_src_reg(inst->src_reg[2]); + mesa_inst->TexSrcUnit = inst->sampler; + mesa_inst->TexSrcTarget = inst->tex_target; + mesa_inst->TexShadow = inst->tex_shadow; + mesa_instruction_annotation[i] = inst->ir; + + /* Set IndirectRegisterFiles. */ + if (mesa_inst->DstReg.RelAddr) + prog->IndirectRegisterFiles |= 1 << mesa_inst->DstReg.File; + + /* Update program's bitmask of indirectly accessed register files */ + 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) { + case OPCODE_BGNSUB: + inst->function->inst = i; + mesa_inst->Comment = strdup(inst->function->sig->function_name()); + break; + case OPCODE_ENDSUB: + mesa_inst->Comment = strdup(inst->function->sig->function_name()); + break; + case OPCODE_CAL: + mesa_inst->BranchTarget = inst->function->sig_id; /* rewritten later */ + break; + case OPCODE_ARL: + prog->NumAddressRegs = 1; + break; + default: + break; + } + + mesa_inst++; + i++; + + if (!shader_program->LinkStatus) + break; + } + + if (!shader_program->LinkStatus) { + free(mesa_instructions); + _mesa_reference_program(ctx, &shader->Program, NULL); + return NULL; + } + + set_branchtargets(&v, mesa_instructions, num_instructions); + + if (ctx->Shader.Flags & GLSL_DUMP) { + 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); + } + + prog->Instructions = mesa_instructions; + prog->NumInstructions = num_instructions; + + do_set_program_inouts(shader->ir, prog); + count_resources(prog); + + check_resources(ctx, shader_program, prog); + + _mesa_reference_program(ctx, &shader->Program, prog); + + if ((ctx->Shader.Flags & GLSL_NO_OPT) == 0) { + _mesa_optimize_program(ctx, prog); + } + + return prog; +} + +extern "C" { + +/** + * Link a shader. + * Called via ctx->Driver.LinkShader() + * This actually involves converting GLSL IR into Mesa gl_programs with + * code lowering and other optimizations. + */ +GLboolean +_mesa_ir_link_shader(struct gl_context *ctx, struct gl_shader_program *prog) +{ + assert(prog->LinkStatus); + + for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) { + if (prog->_LinkedShaders[i] == NULL) + continue; + + bool progress; + exec_list *ir = prog->_LinkedShaders[i]->ir; + const 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); + lower_instructions(ir, (MOD_TO_FRACT | DIV_TO_MUL_RCP | EXP_TO_EXP2 + | LOG_TO_LOG2 + | ((options->EmitNoPow) ? POW_TO_EXP2 : 0))); + + progress = do_lower_jumps(ir, true, true, options->EmitNoMainReturn, options->EmitNoCont, options->EmitNoLoops) || progress; + + progress = do_common_optimization(ir, true, options->MaxUnrollIterations) || progress; + + progress = lower_quadop_vector(ir, true) || progress; + + if (options->EmitNoIfs) { + progress = lower_discard(ir) || progress; + progress = lower_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 < MESA_SHADER_TYPES; i++) { + struct gl_program *linked_prog; + + if (prog->_LinkedShaders[i] == NULL) + continue; + + linked_prog = get_mesa_program(ctx, prog, prog->_LinkedShaders[i]); + + if (linked_prog) { + bool ok = true; + + 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; + case GL_GEOMETRY_SHADER: + _mesa_reference_geomprog(ctx, &prog->GeometryProgram, + (struct gl_geometry_program *)linked_prog); + ok = ctx->Driver.ProgramStringNotify(ctx, GL_GEOMETRY_PROGRAM_NV, + linked_prog); + break; + } + if (!ok) { + return GL_FALSE; + } + } + + _mesa_reference_program(ctx, &linked_prog, NULL); + } + + return GL_TRUE; +} + + +/** + * Compile a GLSL shader. Called via glCompileShader(). + */ +void +_mesa_glsl_compile_shader(struct gl_context *ctx, struct gl_shader *shader) +{ + struct _mesa_glsl_parse_state *state = + 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->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); + _mesa_glsl_parse(state); + _mesa_glsl_lexer_dtor(state); + } + + ralloc_free(shader->ir); + shader->ir = new(shader) exec_list; + if (!state->error && !state->translation_unit.is_empty()) + _mesa_ast_to_hir(shader->ir, state); + + if (!state->error && !shader->ir->is_empty()) { + validate_ir_tree(shader->ir); + + /* 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); + } + + shader->symbols = state->symbols; + + shader->CompileStatus = !state->error; + shader->InfoLog = state->info_log; + shader->Version = state->language_version; + memcpy(shader->builtins_to_link, state->builtins_to_link, + sizeof(shader->builtins_to_link[0]) * state->num_builtins_to_link); + shader->num_builtins_to_link = state->num_builtins_to_link; + + if (ctx->Shader.Flags & GLSL_LOG) { + _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->ir); + + ralloc_free(state); +} + + +/** + * Link a GLSL shader program. Called via glLinkProgram(). + */ +void +_mesa_glsl_link_shader(struct gl_context *ctx, struct gl_shader_program *prog) +{ + unsigned int i; + + _mesa_clear_shader_program_data(ctx, prog); + + prog->LinkStatus = GL_TRUE; + + for (i = 0; i < prog->NumShaders; i++) { + if (!prog->Shaders[i]->CompileStatus) { + fail_link(prog, "linking with uncompiled shader"); + prog->LinkStatus = GL_FALSE; + } + } + + prog->Varying = _mesa_new_parameter_list(); + _mesa_reference_vertprog(ctx, &prog->VertexProgram, NULL); + _mesa_reference_fragprog(ctx, &prog->FragmentProgram, NULL); + _mesa_reference_geomprog(ctx, &prog->GeometryProgram, NULL); + + if (prog->LinkStatus) { + link_shaders(ctx, prog); + } + + if (prog->LinkStatus) { + 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); + } + } +} + +} /* extern "C" */ diff --git a/mesalib/src/mesa/program/prog_statevars.c b/mesalib/src/mesa/program/prog_statevars.c index df3c76d18..384aa2cb2 100644 --- a/mesalib/src/mesa/program/prog_statevars.c +++ b/mesalib/src/mesa/program/prog_statevars.c @@ -1,1182 +1,1208 @@ -/* - * Mesa 3-D graphics library - * Version: 7.1 - * - * Copyright (C) 1999-2007 Brian Paul All Rights Reserved. - * - * 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 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 - * BRIAN PAUL 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 prog_statevars.c - * Program state variable management. - * \author Brian Paul - */ - - -#include "main/glheader.h" -#include "main/context.h" -#include "main/imports.h" -#include "main/macros.h" -#include "main/mtypes.h" -#include "prog_statevars.h" -#include "prog_parameter.h" - - -/** - * Use the list of tokens in the state[] array to find global GL state - * and return it in . Usually, four values are returned in - * but matrix queries may return as many as 16 values. - * This function is used for ARB vertex/fragment programs. - * The program parser will produce the state[] values. - */ -static void -_mesa_fetch_state(struct gl_context *ctx, const gl_state_index state[], - GLfloat *value) -{ - switch (state[0]) { - case STATE_MATERIAL: - { - /* state[1] is either 0=front or 1=back side */ - const GLuint face = (GLuint) state[1]; - const struct gl_material *mat = &ctx->Light.Material; - ASSERT(face == 0 || face == 1); - /* we rely on tokens numbered so that _BACK_ == _FRONT_+ 1 */ - ASSERT(MAT_ATTRIB_FRONT_AMBIENT + 1 == MAT_ATTRIB_BACK_AMBIENT); - /* XXX we could get rid of this switch entirely with a little - * work in arbprogparse.c's parse_state_single_item(). - */ - /* state[2] is the material attribute */ - switch (state[2]) { - case STATE_AMBIENT: - COPY_4V(value, mat->Attrib[MAT_ATTRIB_FRONT_AMBIENT + face]); - return; - case STATE_DIFFUSE: - COPY_4V(value, mat->Attrib[MAT_ATTRIB_FRONT_DIFFUSE + face]); - return; - case STATE_SPECULAR: - COPY_4V(value, mat->Attrib[MAT_ATTRIB_FRONT_SPECULAR + face]); - return; - case STATE_EMISSION: - COPY_4V(value, mat->Attrib[MAT_ATTRIB_FRONT_EMISSION + face]); - return; - case STATE_SHININESS: - value[0] = mat->Attrib[MAT_ATTRIB_FRONT_SHININESS + face][0]; - value[1] = 0.0F; - value[2] = 0.0F; - value[3] = 1.0F; - return; - default: - _mesa_problem(ctx, "Invalid material state in fetch_state"); - return; - } - } - case STATE_LIGHT: - { - /* state[1] is the light number */ - const GLuint ln = (GLuint) state[1]; - /* state[2] is the light attribute */ - switch (state[2]) { - case STATE_AMBIENT: - COPY_4V(value, ctx->Light.Light[ln].Ambient); - return; - case STATE_DIFFUSE: - COPY_4V(value, ctx->Light.Light[ln].Diffuse); - return; - case STATE_SPECULAR: - COPY_4V(value, ctx->Light.Light[ln].Specular); - return; - case STATE_POSITION: - COPY_4V(value, ctx->Light.Light[ln].EyePosition); - return; - case STATE_ATTENUATION: - value[0] = ctx->Light.Light[ln].ConstantAttenuation; - value[1] = ctx->Light.Light[ln].LinearAttenuation; - value[2] = ctx->Light.Light[ln].QuadraticAttenuation; - value[3] = ctx->Light.Light[ln].SpotExponent; - return; - case STATE_SPOT_DIRECTION: - COPY_3V(value, ctx->Light.Light[ln].SpotDirection); - value[3] = ctx->Light.Light[ln]._CosCutoff; - return; - case STATE_SPOT_CUTOFF: - value[0] = ctx->Light.Light[ln].SpotCutoff; - return; - case STATE_HALF_VECTOR: - { - static const GLfloat eye_z[] = {0, 0, 1}; - GLfloat p[3]; - /* Compute infinite half angle vector: - * halfVector = normalize(normalize(lightPos) + (0, 0, 1)) - * light.EyePosition.w should be 0 for infinite lights. - */ - COPY_3V(p, ctx->Light.Light[ln].EyePosition); - NORMALIZE_3FV(p); - ADD_3V(value, p, eye_z); - NORMALIZE_3FV(value); - value[3] = 1.0; - } - return; - default: - _mesa_problem(ctx, "Invalid light state in fetch_state"); - return; - } - } - case STATE_LIGHTMODEL_AMBIENT: - COPY_4V(value, ctx->Light.Model.Ambient); - return; - case STATE_LIGHTMODEL_SCENECOLOR: - if (state[1] == 0) { - /* front */ - GLint i; - for (i = 0; i < 3; i++) { - value[i] = ctx->Light.Model.Ambient[i] - * ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_AMBIENT][i] - + ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_EMISSION][i]; - } - value[3] = ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_DIFFUSE][3]; - } - else { - /* back */ - GLint i; - for (i = 0; i < 3; i++) { - value[i] = ctx->Light.Model.Ambient[i] - * ctx->Light.Material.Attrib[MAT_ATTRIB_BACK_AMBIENT][i] - + ctx->Light.Material.Attrib[MAT_ATTRIB_BACK_EMISSION][i]; - } - value[3] = ctx->Light.Material.Attrib[MAT_ATTRIB_BACK_DIFFUSE][3]; - } - return; - case STATE_LIGHTPROD: - { - const GLuint ln = (GLuint) state[1]; - const GLuint face = (GLuint) state[2]; - GLint i; - ASSERT(face == 0 || face == 1); - switch (state[3]) { - case STATE_AMBIENT: - for (i = 0; i < 3; i++) { - value[i] = ctx->Light.Light[ln].Ambient[i] * - ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_AMBIENT+face][i]; - } - /* [3] = material alpha */ - value[3] = ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_AMBIENT+face][3]; - return; - case STATE_DIFFUSE: - for (i = 0; i < 3; i++) { - value[i] = ctx->Light.Light[ln].Diffuse[i] * - ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_DIFFUSE+face][i]; - } - /* [3] = material alpha */ - value[3] = ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_DIFFUSE+face][3]; - return; - case STATE_SPECULAR: - for (i = 0; i < 3; i++) { - value[i] = ctx->Light.Light[ln].Specular[i] * - ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_SPECULAR+face][i]; - } - /* [3] = material alpha */ - value[3] = ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_SPECULAR+face][3]; - return; - default: - _mesa_problem(ctx, "Invalid lightprod state in fetch_state"); - return; - } - } - case STATE_TEXGEN: - { - /* state[1] is the texture unit */ - const GLuint unit = (GLuint) state[1]; - /* state[2] is the texgen attribute */ - switch (state[2]) { - case STATE_TEXGEN_EYE_S: - COPY_4V(value, ctx->Texture.Unit[unit].GenS.EyePlane); - return; - case STATE_TEXGEN_EYE_T: - COPY_4V(value, ctx->Texture.Unit[unit].GenT.EyePlane); - return; - case STATE_TEXGEN_EYE_R: - COPY_4V(value, ctx->Texture.Unit[unit].GenR.EyePlane); - return; - case STATE_TEXGEN_EYE_Q: - COPY_4V(value, ctx->Texture.Unit[unit].GenQ.EyePlane); - return; - case STATE_TEXGEN_OBJECT_S: - COPY_4V(value, ctx->Texture.Unit[unit].GenS.ObjectPlane); - return; - case STATE_TEXGEN_OBJECT_T: - COPY_4V(value, ctx->Texture.Unit[unit].GenT.ObjectPlane); - return; - case STATE_TEXGEN_OBJECT_R: - COPY_4V(value, ctx->Texture.Unit[unit].GenR.ObjectPlane); - return; - case STATE_TEXGEN_OBJECT_Q: - COPY_4V(value, ctx->Texture.Unit[unit].GenQ.ObjectPlane); - return; - default: - _mesa_problem(ctx, "Invalid texgen state in fetch_state"); - return; - } - } - case STATE_TEXENV_COLOR: - { - /* state[1] is the texture unit */ - const GLuint unit = (GLuint) state[1]; - COPY_4V(value, ctx->Texture.Unit[unit].EnvColor); - } - return; - case STATE_FOG_COLOR: - COPY_4V(value, ctx->Fog.Color); - return; - case STATE_FOG_PARAMS: - value[0] = ctx->Fog.Density; - value[1] = ctx->Fog.Start; - value[2] = ctx->Fog.End; - value[3] = (ctx->Fog.End == ctx->Fog.Start) - ? 1.0f : (GLfloat)(1.0 / (ctx->Fog.End - ctx->Fog.Start)); - return; - case STATE_CLIPPLANE: - { - const GLuint plane = (GLuint) state[1]; - COPY_4V(value, ctx->Transform.EyeUserPlane[plane]); - } - return; - case STATE_POINT_SIZE: - value[0] = ctx->Point.Size; - value[1] = ctx->Point.MinSize; - value[2] = ctx->Point.MaxSize; - value[3] = ctx->Point.Threshold; - return; - case STATE_POINT_ATTENUATION: - value[0] = ctx->Point.Params[0]; - value[1] = ctx->Point.Params[1]; - value[2] = ctx->Point.Params[2]; - value[3] = 1.0F; - return; - case STATE_MODELVIEW_MATRIX: - case STATE_PROJECTION_MATRIX: - case STATE_MVP_MATRIX: - case STATE_TEXTURE_MATRIX: - case STATE_PROGRAM_MATRIX: - { - /* state[0] = modelview, projection, texture, etc. */ - /* state[1] = which texture matrix or program matrix */ - /* state[2] = first row to fetch */ - /* state[3] = last row to fetch */ - /* state[4] = transpose, inverse or invtrans */ - const GLmatrix *matrix; - const gl_state_index mat = state[0]; - const GLuint index = (GLuint) state[1]; - const GLuint firstRow = (GLuint) state[2]; - const GLuint lastRow = (GLuint) state[3]; - const gl_state_index modifier = state[4]; - const GLfloat *m; - GLuint row, i; - ASSERT(firstRow >= 0); - ASSERT(firstRow < 4); - ASSERT(lastRow >= 0); - ASSERT(lastRow < 4); - if (mat == STATE_MODELVIEW_MATRIX) { - matrix = ctx->ModelviewMatrixStack.Top; - } - else if (mat == STATE_PROJECTION_MATRIX) { - matrix = ctx->ProjectionMatrixStack.Top; - } - else if (mat == STATE_MVP_MATRIX) { - matrix = &ctx->_ModelProjectMatrix; - } - else if (mat == STATE_TEXTURE_MATRIX) { - ASSERT(index < Elements(ctx->TextureMatrixStack)); - matrix = ctx->TextureMatrixStack[index].Top; - } - else if (mat == STATE_PROGRAM_MATRIX) { - ASSERT(index < Elements(ctx->ProgramMatrixStack)); - matrix = ctx->ProgramMatrixStack[index].Top; - } - else { - _mesa_problem(ctx, "Bad matrix name in _mesa_fetch_state()"); - return; - } - if (modifier == STATE_MATRIX_INVERSE || - modifier == STATE_MATRIX_INVTRANS) { - /* Be sure inverse is up to date: - */ - _math_matrix_alloc_inv( (GLmatrix *) matrix ); - _math_matrix_analyse( (GLmatrix*) matrix ); - m = matrix->inv; - } - else { - m = matrix->m; - } - if (modifier == STATE_MATRIX_TRANSPOSE || - modifier == STATE_MATRIX_INVTRANS) { - for (i = 0, row = firstRow; row <= lastRow; row++) { - value[i++] = m[row * 4 + 0]; - value[i++] = m[row * 4 + 1]; - value[i++] = m[row * 4 + 2]; - value[i++] = m[row * 4 + 3]; - } - } - else { - for (i = 0, row = firstRow; row <= lastRow; row++) { - value[i++] = m[row + 0]; - value[i++] = m[row + 4]; - value[i++] = m[row + 8]; - value[i++] = m[row + 12]; - } - } - } - return; - case STATE_DEPTH_RANGE: - value[0] = ctx->Viewport.Near; /* near */ - value[1] = ctx->Viewport.Far; /* far */ - value[2] = ctx->Viewport.Far - ctx->Viewport.Near; /* far - near */ - value[3] = 1.0; - return; - case STATE_FRAGMENT_PROGRAM: - { - /* state[1] = {STATE_ENV, STATE_LOCAL} */ - /* state[2] = parameter index */ - const int idx = (int) state[2]; - switch (state[1]) { - case STATE_ENV: - COPY_4V(value, ctx->FragmentProgram.Parameters[idx]); - return; - case STATE_LOCAL: - COPY_4V(value, ctx->FragmentProgram.Current->Base.LocalParams[idx]); - return; - default: - _mesa_problem(ctx, "Bad state switch in _mesa_fetch_state()"); - return; - } - } - return; - - case STATE_VERTEX_PROGRAM: - { - /* state[1] = {STATE_ENV, STATE_LOCAL} */ - /* state[2] = parameter index */ - const int idx = (int) state[2]; - switch (state[1]) { - case STATE_ENV: - COPY_4V(value, ctx->VertexProgram.Parameters[idx]); - return; - case STATE_LOCAL: - COPY_4V(value, ctx->VertexProgram.Current->Base.LocalParams[idx]); - return; - default: - _mesa_problem(ctx, "Bad state switch in _mesa_fetch_state()"); - return; - } - } - return; - - case STATE_NORMAL_SCALE: - ASSIGN_4V(value, ctx->_ModelViewInvScale, 0, 0, 1); - return; - - case STATE_INTERNAL: - switch (state[1]) { - case STATE_CURRENT_ATTRIB: - { - const GLuint idx = (GLuint) state[2]; - COPY_4V(value, ctx->Current.Attrib[idx]); - } - return; - - case STATE_NORMAL_SCALE: - ASSIGN_4V(value, - ctx->_ModelViewInvScale, - ctx->_ModelViewInvScale, - ctx->_ModelViewInvScale, - 1); - return; - - case STATE_TEXRECT_SCALE: - /* Value = { 1/texWidth, 1/texHeight, 0, 1 }. - * Used to convert unnormalized texcoords to normalized texcoords. - */ - { - const int unit = (int) state[2]; - const struct gl_texture_object *texObj - = ctx->Texture.Unit[unit]._Current; - if (texObj) { - struct gl_texture_image *texImage = texObj->Image[0][0]; - ASSIGN_4V(value, - (GLfloat) (1.0 / texImage->Width), - (GLfloat) (1.0 / texImage->Height), - 0.0f, 1.0f); - } - } - return; - - case STATE_FOG_PARAMS_OPTIMIZED: - /* for simpler per-vertex/pixel fog calcs. POW (for EXP/EXP2 fog) - * might be more expensive than EX2 on some hw, plus it needs - * another constant (e) anyway. Linear fog can now be done with a - * single MAD. - * linear: fogcoord * -1/(end-start) + end/(end-start) - * exp: 2^-(density/ln(2) * fogcoord) - * exp2: 2^-((density/(ln(2)^2) * fogcoord)^2) - */ - value[0] = (ctx->Fog.End == ctx->Fog.Start) - ? 1.0f : (GLfloat)(-1.0F / (ctx->Fog.End - ctx->Fog.Start)); - value[1] = ctx->Fog.End * -value[0]; - value[2] = (GLfloat)(ctx->Fog.Density * ONE_DIV_LN2); - value[3] = (GLfloat)(ctx->Fog.Density * ONE_DIV_SQRT_LN2); - return; - - case STATE_POINT_SIZE_CLAMPED: - { - /* this includes implementation dependent limits, to avoid - * another potentially necessary clamp. - * Note: for sprites, point smooth (point AA) is ignored - * and we'll clamp to MinPointSizeAA and MaxPointSize, because we - * expect drivers will want to say their minimum for AA size is 0.0 - * but for non-AA it's 1.0 (because normal points with size below 1.0 - * need to get rounded up to 1.0, hence never disappear). GL does - * not specify max clamp size for sprites, other than it needs to be - * at least as large as max AA size, hence use non-AA size there. - */ - GLfloat minImplSize; - GLfloat maxImplSize; - if (ctx->Point.PointSprite) { - minImplSize = ctx->Const.MinPointSizeAA; - maxImplSize = ctx->Const.MaxPointSize; - } - else if (ctx->Point.SmoothFlag || ctx->Multisample._Enabled) { - minImplSize = ctx->Const.MinPointSizeAA; - maxImplSize = ctx->Const.MaxPointSizeAA; - } - else { - minImplSize = ctx->Const.MinPointSize; - maxImplSize = ctx->Const.MaxPointSize; - } - value[0] = ctx->Point.Size; - value[1] = ctx->Point.MinSize >= minImplSize ? ctx->Point.MinSize : minImplSize; - value[2] = ctx->Point.MaxSize <= maxImplSize ? ctx->Point.MaxSize : maxImplSize; - value[3] = ctx->Point.Threshold; - } - return; - case STATE_POINT_SIZE_IMPL_CLAMP: - { - /* for implementation clamp only in vs */ - GLfloat minImplSize; - GLfloat maxImplSize; - if (ctx->Point.PointSprite) { - minImplSize = ctx->Const.MinPointSizeAA; - maxImplSize = ctx->Const.MaxPointSize; - } - else if (ctx->Point.SmoothFlag || ctx->Multisample._Enabled) { - minImplSize = ctx->Const.MinPointSizeAA; - maxImplSize = ctx->Const.MaxPointSizeAA; - } - else { - minImplSize = ctx->Const.MinPointSize; - maxImplSize = ctx->Const.MaxPointSize; - } - value[0] = ctx->Point.Size; - value[1] = minImplSize; - value[2] = maxImplSize; - value[3] = ctx->Point.Threshold; - } - return; - case STATE_LIGHT_SPOT_DIR_NORMALIZED: - { - /* here, state[2] is the light number */ - /* pre-normalize spot dir */ - const GLuint ln = (GLuint) state[2]; - COPY_3V(value, ctx->Light.Light[ln]._NormSpotDirection); - value[3] = ctx->Light.Light[ln]._CosCutoff; - } - return; - - case STATE_LIGHT_POSITION: - { - const GLuint ln = (GLuint) state[2]; - COPY_4V(value, ctx->Light.Light[ln]._Position); - } - return; - - case STATE_LIGHT_POSITION_NORMALIZED: - { - const GLuint ln = (GLuint) state[2]; - COPY_4V(value, ctx->Light.Light[ln]._Position); - NORMALIZE_3FV( value ); - } - return; - - case STATE_LIGHT_HALF_VECTOR: - { - const GLuint ln = (GLuint) state[2]; - GLfloat p[3]; - /* Compute infinite half angle vector: - * halfVector = normalize(normalize(lightPos) + (0, 0, 1)) - * light.EyePosition.w should be 0 for infinite lights. - */ - COPY_3V(p, ctx->Light.Light[ln]._Position); - NORMALIZE_3FV(p); - ADD_3V(value, p, ctx->_EyeZDir); - NORMALIZE_3FV(value); - value[3] = 1.0; - } - return; - - case STATE_PT_SCALE: - value[0] = ctx->Pixel.RedScale; - value[1] = ctx->Pixel.GreenScale; - value[2] = ctx->Pixel.BlueScale; - value[3] = ctx->Pixel.AlphaScale; - return; - - case STATE_PT_BIAS: - value[0] = ctx->Pixel.RedBias; - value[1] = ctx->Pixel.GreenBias; - value[2] = ctx->Pixel.BlueBias; - value[3] = ctx->Pixel.AlphaBias; - return; - - case STATE_SHADOW_AMBIENT: - { - const int unit = (int) state[2]; - const struct gl_texture_object *texObj - = ctx->Texture.Unit[unit]._Current; - if (texObj) { - value[0] = - value[1] = - value[2] = - value[3] = texObj->CompareFailValue; - } - } - return; - - case STATE_FB_SIZE: - value[0] = (GLfloat) (ctx->DrawBuffer->Width - 1); - value[1] = (GLfloat) (ctx->DrawBuffer->Height - 1); - value[2] = 0.0F; - value[3] = 0.0F; - return; - - case STATE_FB_WPOS_Y_TRANSFORM: - /* A driver may negate this conditional by using ZW swizzle - * instead of XY (based on e.g. some other state). */ - if (ctx->DrawBuffer->Name != 0) { - /* Identity (XY) followed by flipping Y upside down (ZW). */ - value[0] = 1.0F; - value[1] = 0.0F; - value[2] = -1.0F; - value[3] = (GLfloat) (ctx->DrawBuffer->Height - 1); - } else { - /* Flipping Y upside down (XY) followed by identity (ZW). */ - value[0] = -1.0F; - value[1] = (GLfloat) (ctx->DrawBuffer->Height - 1); - value[2] = 1.0F; - value[3] = 0.0F; - } - return; - - case STATE_ROT_MATRIX_0: - { - const int unit = (int) state[2]; - GLfloat *rotMat22 = ctx->Texture.Unit[unit].RotMatrix; - value[0] = rotMat22[0]; - value[1] = rotMat22[2]; - value[2] = 0.0; - value[3] = 0.0; - } - return; - - case STATE_ROT_MATRIX_1: - { - const int unit = (int) state[2]; - GLfloat *rotMat22 = ctx->Texture.Unit[unit].RotMatrix; - value[0] = rotMat22[1]; - value[1] = rotMat22[3]; - value[2] = 0.0; - value[3] = 0.0; - } - return; - - /* XXX: make sure new tokens added here are also handled in the - * _mesa_program_state_flags() switch, below. - */ - default: - /* Unknown state indexes are silently ignored here. - * Drivers may do something special. - */ - return; - } - return; - - default: - _mesa_problem(ctx, "Invalid state in _mesa_fetch_state"); - return; - } -} - - -/** - * Return a bitmask of the Mesa state flags (_NEW_* values) which would - * indicate that the given context state may have changed. - * The bitmask is used during validation to determine if we need to update - * vertex/fragment program parameters (like "state.material.color") when - * some GL state has changed. - */ -GLbitfield -_mesa_program_state_flags(const gl_state_index state[STATE_LENGTH]) -{ - switch (state[0]) { - case STATE_MATERIAL: - case STATE_LIGHT: - case STATE_LIGHTMODEL_AMBIENT: - case STATE_LIGHTMODEL_SCENECOLOR: - case STATE_LIGHTPROD: - return _NEW_LIGHT; - - case STATE_TEXGEN: - case STATE_TEXENV_COLOR: - return _NEW_TEXTURE; - - case STATE_FOG_COLOR: - case STATE_FOG_PARAMS: - return _NEW_FOG; - - case STATE_CLIPPLANE: - return _NEW_TRANSFORM; - - case STATE_POINT_SIZE: - case STATE_POINT_ATTENUATION: - return _NEW_POINT; - - case STATE_MODELVIEW_MATRIX: - return _NEW_MODELVIEW; - case STATE_PROJECTION_MATRIX: - return _NEW_PROJECTION; - case STATE_MVP_MATRIX: - return _NEW_MODELVIEW | _NEW_PROJECTION; - case STATE_TEXTURE_MATRIX: - return _NEW_TEXTURE_MATRIX; - case STATE_PROGRAM_MATRIX: - return _NEW_TRACK_MATRIX; - - case STATE_DEPTH_RANGE: - return _NEW_VIEWPORT; - - case STATE_FRAGMENT_PROGRAM: - case STATE_VERTEX_PROGRAM: - return _NEW_PROGRAM; - - case STATE_NORMAL_SCALE: - return _NEW_MODELVIEW; - - case STATE_INTERNAL: - switch (state[1]) { - case STATE_CURRENT_ATTRIB: - return _NEW_CURRENT_ATTRIB; - - case STATE_NORMAL_SCALE: - return _NEW_MODELVIEW; - - case STATE_TEXRECT_SCALE: - case STATE_SHADOW_AMBIENT: - case STATE_ROT_MATRIX_0: - case STATE_ROT_MATRIX_1: - return _NEW_TEXTURE; - case STATE_FOG_PARAMS_OPTIMIZED: - return _NEW_FOG; - case STATE_POINT_SIZE_CLAMPED: - case STATE_POINT_SIZE_IMPL_CLAMP: - return _NEW_POINT | _NEW_MULTISAMPLE; - case STATE_LIGHT_SPOT_DIR_NORMALIZED: - case STATE_LIGHT_POSITION: - case STATE_LIGHT_POSITION_NORMALIZED: - case STATE_LIGHT_HALF_VECTOR: - return _NEW_LIGHT; - - case STATE_PT_SCALE: - case STATE_PT_BIAS: - return _NEW_PIXEL; - - case STATE_FB_SIZE: - case STATE_FB_WPOS_Y_TRANSFORM: - return _NEW_BUFFERS; - - default: - /* unknown state indexes are silently ignored and - * no flag set, since it is handled by the driver. - */ - return 0; - } - - default: - _mesa_problem(NULL, "unexpected state[0] in make_state_flags()"); - return 0; - } -} - - -static void -append(char *dst, const char *src) -{ - while (*dst) - dst++; - while (*src) - *dst++ = *src++; - *dst = 0; -} - - -/** - * Convert token 'k' to a string, append it onto 'dst' string. - */ -static void -append_token(char *dst, gl_state_index k) -{ - switch (k) { - case STATE_MATERIAL: - append(dst, "material"); - break; - case STATE_LIGHT: - append(dst, "light"); - break; - case STATE_LIGHTMODEL_AMBIENT: - append(dst, "lightmodel.ambient"); - break; - case STATE_LIGHTMODEL_SCENECOLOR: - break; - case STATE_LIGHTPROD: - append(dst, "lightprod"); - break; - case STATE_TEXGEN: - append(dst, "texgen"); - break; - case STATE_FOG_COLOR: - append(dst, "fog.color"); - break; - case STATE_FOG_PARAMS: - append(dst, "fog.params"); - break; - case STATE_CLIPPLANE: - append(dst, "clip"); - break; - case STATE_POINT_SIZE: - append(dst, "point.size"); - break; - case STATE_POINT_ATTENUATION: - append(dst, "point.attenuation"); - break; - case STATE_MODELVIEW_MATRIX: - append(dst, "matrix.modelview"); - break; - case STATE_PROJECTION_MATRIX: - append(dst, "matrix.projection"); - break; - case STATE_MVP_MATRIX: - append(dst, "matrix.mvp"); - break; - case STATE_TEXTURE_MATRIX: - append(dst, "matrix.texture"); - break; - case STATE_PROGRAM_MATRIX: - append(dst, "matrix.program"); - break; - case STATE_MATRIX_INVERSE: - append(dst, ".inverse"); - break; - case STATE_MATRIX_TRANSPOSE: - append(dst, ".transpose"); - break; - case STATE_MATRIX_INVTRANS: - append(dst, ".invtrans"); - break; - case STATE_AMBIENT: - append(dst, ".ambient"); - break; - case STATE_DIFFUSE: - append(dst, ".diffuse"); - break; - case STATE_SPECULAR: - append(dst, ".specular"); - break; - case STATE_EMISSION: - append(dst, ".emission"); - break; - case STATE_SHININESS: - append(dst, "lshininess"); - break; - case STATE_HALF_VECTOR: - append(dst, ".half"); - break; - case STATE_POSITION: - append(dst, ".position"); - break; - case STATE_ATTENUATION: - append(dst, ".attenuation"); - break; - case STATE_SPOT_DIRECTION: - append(dst, ".spot.direction"); - break; - case STATE_SPOT_CUTOFF: - append(dst, ".spot.cutoff"); - break; - case STATE_TEXGEN_EYE_S: - append(dst, ".eye.s"); - break; - case STATE_TEXGEN_EYE_T: - append(dst, ".eye.t"); - break; - case STATE_TEXGEN_EYE_R: - append(dst, ".eye.r"); - break; - case STATE_TEXGEN_EYE_Q: - append(dst, ".eye.q"); - break; - case STATE_TEXGEN_OBJECT_S: - append(dst, ".object.s"); - break; - case STATE_TEXGEN_OBJECT_T: - append(dst, ".object.t"); - break; - case STATE_TEXGEN_OBJECT_R: - append(dst, ".object.r"); - break; - case STATE_TEXGEN_OBJECT_Q: - append(dst, ".object.q"); - break; - case STATE_TEXENV_COLOR: - append(dst, "texenv"); - break; - case STATE_DEPTH_RANGE: - append(dst, "depth.range"); - break; - case STATE_VERTEX_PROGRAM: - case STATE_FRAGMENT_PROGRAM: - break; - case STATE_ENV: - append(dst, "env"); - break; - case STATE_LOCAL: - append(dst, "local"); - break; - /* BEGIN internal state vars */ - case STATE_INTERNAL: - append(dst, ".internal."); - break; - case STATE_CURRENT_ATTRIB: - append(dst, "current"); - break; - case STATE_NORMAL_SCALE: - append(dst, "normalScale"); - break; - case STATE_TEXRECT_SCALE: - append(dst, "texrectScale"); - break; - case STATE_FOG_PARAMS_OPTIMIZED: - append(dst, "fogParamsOptimized"); - break; - case STATE_POINT_SIZE_CLAMPED: - append(dst, "pointSizeClamped"); - break; - case STATE_POINT_SIZE_IMPL_CLAMP: - append(dst, "pointSizeImplClamp"); - break; - case STATE_LIGHT_SPOT_DIR_NORMALIZED: - append(dst, "lightSpotDirNormalized"); - break; - case STATE_LIGHT_POSITION: - append(dst, "lightPosition"); - break; - case STATE_LIGHT_POSITION_NORMALIZED: - append(dst, "light.position.normalized"); - break; - case STATE_LIGHT_HALF_VECTOR: - append(dst, "lightHalfVector"); - break; - case STATE_PT_SCALE: - append(dst, "PTscale"); - break; - case STATE_PT_BIAS: - append(dst, "PTbias"); - break; - case STATE_SHADOW_AMBIENT: - append(dst, "CompareFailValue"); - break; - case STATE_FB_SIZE: - append(dst, "FbSize"); - break; - case STATE_FB_WPOS_Y_TRANSFORM: - append(dst, "FbWposYTransform"); - break; - case STATE_ROT_MATRIX_0: - append(dst, "rotMatrixRow0"); - break; - case STATE_ROT_MATRIX_1: - append(dst, "rotMatrixRow1"); - break; - default: - /* probably STATE_INTERNAL_DRIVER+i (driver private state) */ - append(dst, "driverState"); - } -} - -static void -append_face(char *dst, GLint face) -{ - if (face == 0) - append(dst, "front."); - else - append(dst, "back."); -} - -static void -append_index(char *dst, GLint index) -{ - char s[20]; - sprintf(s, "[%d]", index); - append(dst, s); -} - -/** - * Make a string from the given state vector. - * For example, return "state.matrix.texture[2].inverse". - * Use free() to deallocate the string. - */ -char * -_mesa_program_state_string(const gl_state_index state[STATE_LENGTH]) -{ - char str[1000] = ""; - char tmp[30]; - - append(str, "state."); - append_token(str, state[0]); - - switch (state[0]) { - case STATE_MATERIAL: - append_face(str, state[1]); - append_token(str, state[2]); - break; - case STATE_LIGHT: - append_index(str, state[1]); /* light number [i]. */ - append_token(str, state[2]); /* coefficients */ - break; - case STATE_LIGHTMODEL_AMBIENT: - append(str, "lightmodel.ambient"); - break; - case STATE_LIGHTMODEL_SCENECOLOR: - if (state[1] == 0) { - append(str, "lightmodel.front.scenecolor"); - } - else { - append(str, "lightmodel.back.scenecolor"); - } - break; - case STATE_LIGHTPROD: - append_index(str, state[1]); /* light number [i]. */ - append_face(str, state[2]); - append_token(str, state[3]); - break; - case STATE_TEXGEN: - append_index(str, state[1]); /* tex unit [i] */ - append_token(str, state[2]); /* plane coef */ - break; - case STATE_TEXENV_COLOR: - append_index(str, state[1]); /* tex unit [i] */ - append(str, "color"); - break; - case STATE_CLIPPLANE: - append_index(str, state[1]); /* plane [i] */ - append(str, ".plane"); - break; - case STATE_MODELVIEW_MATRIX: - case STATE_PROJECTION_MATRIX: - case STATE_MVP_MATRIX: - case STATE_TEXTURE_MATRIX: - case STATE_PROGRAM_MATRIX: - { - /* state[0] = modelview, projection, texture, etc. */ - /* state[1] = which texture matrix or program matrix */ - /* state[2] = first row to fetch */ - /* state[3] = last row to fetch */ - /* state[4] = transpose, inverse or invtrans */ - const gl_state_index mat = state[0]; - const GLuint index = (GLuint) state[1]; - const GLuint firstRow = (GLuint) state[2]; - const GLuint lastRow = (GLuint) state[3]; - const gl_state_index modifier = state[4]; - if (index || - mat == STATE_TEXTURE_MATRIX || - mat == STATE_PROGRAM_MATRIX) - append_index(str, index); - if (modifier) - append_token(str, modifier); - if (firstRow == lastRow) - sprintf(tmp, ".row[%d]", firstRow); - else - sprintf(tmp, ".row[%d..%d]", firstRow, lastRow); - append(str, tmp); - } - break; - case STATE_POINT_SIZE: - break; - case STATE_POINT_ATTENUATION: - break; - case STATE_FOG_PARAMS: - break; - case STATE_FOG_COLOR: - break; - case STATE_DEPTH_RANGE: - break; - case STATE_FRAGMENT_PROGRAM: - case STATE_VERTEX_PROGRAM: - /* state[1] = {STATE_ENV, STATE_LOCAL} */ - /* state[2] = parameter index */ - append_token(str, state[1]); - append_index(str, state[2]); - break; - case STATE_NORMAL_SCALE: - break; - case STATE_INTERNAL: - append_token(str, state[1]); - if (state[1] == STATE_CURRENT_ATTRIB) - append_index(str, state[2]); - break; - default: - _mesa_problem(NULL, "Invalid state in _mesa_program_state_string"); - break; - } - - return _mesa_strdup(str); -} - - -/** - * Loop over all the parameters in a parameter list. If the parameter - * is a GL state reference, look up the current value of that state - * variable and put it into the parameter's Value[4] array. - * Other parameter types never change or are explicitly set by the user - * with glUniform() or glProgramParameter(), etc. - * This would be called at glBegin time. - */ -void -_mesa_load_state_parameters(struct gl_context *ctx, - struct gl_program_parameter_list *paramList) -{ - GLuint i; - - if (!paramList) - return; - - for (i = 0; i < paramList->NumParameters; i++) { - if (paramList->Parameters[i].Type == PROGRAM_STATE_VAR) { - _mesa_fetch_state(ctx, - paramList->Parameters[i].StateIndexes, - paramList->ParameterValues[i]); - } - } -} - - -/** - * Copy the 16 elements of a matrix into four consecutive program - * registers starting at 'pos'. - */ -static void -load_matrix(GLfloat registers[][4], GLuint pos, const GLfloat mat[16]) -{ - GLuint i; - for (i = 0; i < 4; i++) { - registers[pos + i][0] = mat[0 + i]; - registers[pos + i][1] = mat[4 + i]; - registers[pos + i][2] = mat[8 + i]; - registers[pos + i][3] = mat[12 + i]; - } -} - - -/** - * As above, but transpose the matrix. - */ -static void -load_transpose_matrix(GLfloat registers[][4], GLuint pos, - const GLfloat mat[16]) -{ - memcpy(registers[pos], mat, 16 * sizeof(GLfloat)); -} - - -/** - * Load current vertex program's parameter registers with tracked - * matrices (if NV program). This only needs to be done per - * glBegin/glEnd, not per-vertex. - */ -void -_mesa_load_tracked_matrices(struct gl_context *ctx) -{ - GLuint i; - - for (i = 0; i < MAX_NV_VERTEX_PROGRAM_PARAMS / 4; i++) { - /* point 'mat' at source matrix */ - GLmatrix *mat; - if (ctx->VertexProgram.TrackMatrix[i] == GL_MODELVIEW) { - mat = ctx->ModelviewMatrixStack.Top; - } - else if (ctx->VertexProgram.TrackMatrix[i] == GL_PROJECTION) { - mat = ctx->ProjectionMatrixStack.Top; - } - else if (ctx->VertexProgram.TrackMatrix[i] == GL_TEXTURE) { - GLuint unit = MIN2(ctx->Texture.CurrentUnit, - Elements(ctx->TextureMatrixStack) - 1); - mat = ctx->TextureMatrixStack[unit].Top; - } - else if (ctx->VertexProgram.TrackMatrix[i]==GL_MODELVIEW_PROJECTION_NV) { - /* XXX verify the combined matrix is up to date */ - mat = &ctx->_ModelProjectMatrix; - } - else if (ctx->VertexProgram.TrackMatrix[i] >= GL_MATRIX0_NV && - ctx->VertexProgram.TrackMatrix[i] <= GL_MATRIX7_NV) { - GLuint n = ctx->VertexProgram.TrackMatrix[i] - GL_MATRIX0_NV; - ASSERT(n < Elements(ctx->ProgramMatrixStack)); - mat = ctx->ProgramMatrixStack[n].Top; - } - else { - /* no matrix is tracked, but we leave the register values as-is */ - assert(ctx->VertexProgram.TrackMatrix[i] == GL_NONE); - continue; - } - - /* load the matrix values into sequential registers */ - if (ctx->VertexProgram.TrackMatrixTransform[i] == GL_IDENTITY_NV) { - load_matrix(ctx->VertexProgram.Parameters, i*4, mat->m); - } - else if (ctx->VertexProgram.TrackMatrixTransform[i] == GL_INVERSE_NV) { - _math_matrix_analyse(mat); /* update the inverse */ - ASSERT(!_math_matrix_is_dirty(mat)); - load_matrix(ctx->VertexProgram.Parameters, i*4, mat->inv); - } - else if (ctx->VertexProgram.TrackMatrixTransform[i] == GL_TRANSPOSE_NV) { - load_transpose_matrix(ctx->VertexProgram.Parameters, i*4, mat->m); - } - else { - assert(ctx->VertexProgram.TrackMatrixTransform[i] - == GL_INVERSE_TRANSPOSE_NV); - _math_matrix_analyse(mat); /* update the inverse */ - ASSERT(!_math_matrix_is_dirty(mat)); - load_transpose_matrix(ctx->VertexProgram.Parameters, i*4, mat->inv); - } - } -} +/* + * Mesa 3-D graphics library + * Version: 7.1 + * + * Copyright (C) 1999-2007 Brian Paul All Rights Reserved. + * + * 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 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 + * BRIAN PAUL 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 prog_statevars.c + * Program state variable management. + * \author Brian Paul + */ + + +#include "main/glheader.h" +#include "main/context.h" +#include "main/imports.h" +#include "main/macros.h" +#include "main/mtypes.h" +#include "prog_statevars.h" +#include "prog_parameter.h" + + +/** + * Use the list of tokens in the state[] array to find global GL state + * and return it in . Usually, four values are returned in + * but matrix queries may return as many as 16 values. + * This function is used for ARB vertex/fragment programs. + * The program parser will produce the state[] values. + */ +static void +_mesa_fetch_state(struct gl_context *ctx, const gl_state_index state[], + GLfloat *value) +{ + switch (state[0]) { + case STATE_MATERIAL: + { + /* state[1] is either 0=front or 1=back side */ + const GLuint face = (GLuint) state[1]; + const struct gl_material *mat = &ctx->Light.Material; + ASSERT(face == 0 || face == 1); + /* we rely on tokens numbered so that _BACK_ == _FRONT_+ 1 */ + ASSERT(MAT_ATTRIB_FRONT_AMBIENT + 1 == MAT_ATTRIB_BACK_AMBIENT); + /* XXX we could get rid of this switch entirely with a little + * work in arbprogparse.c's parse_state_single_item(). + */ + /* state[2] is the material attribute */ + switch (state[2]) { + case STATE_AMBIENT: + COPY_4V(value, mat->Attrib[MAT_ATTRIB_FRONT_AMBIENT + face]); + return; + case STATE_DIFFUSE: + COPY_4V(value, mat->Attrib[MAT_ATTRIB_FRONT_DIFFUSE + face]); + return; + case STATE_SPECULAR: + COPY_4V(value, mat->Attrib[MAT_ATTRIB_FRONT_SPECULAR + face]); + return; + case STATE_EMISSION: + COPY_4V(value, mat->Attrib[MAT_ATTRIB_FRONT_EMISSION + face]); + return; + case STATE_SHININESS: + value[0] = mat->Attrib[MAT_ATTRIB_FRONT_SHININESS + face][0]; + value[1] = 0.0F; + value[2] = 0.0F; + value[3] = 1.0F; + return; + default: + _mesa_problem(ctx, "Invalid material state in fetch_state"); + return; + } + } + case STATE_LIGHT: + { + /* state[1] is the light number */ + const GLuint ln = (GLuint) state[1]; + /* state[2] is the light attribute */ + switch (state[2]) { + case STATE_AMBIENT: + COPY_4V(value, ctx->Light.Light[ln].Ambient); + return; + case STATE_DIFFUSE: + COPY_4V(value, ctx->Light.Light[ln].Diffuse); + return; + case STATE_SPECULAR: + COPY_4V(value, ctx->Light.Light[ln].Specular); + return; + case STATE_POSITION: + COPY_4V(value, ctx->Light.Light[ln].EyePosition); + return; + case STATE_ATTENUATION: + value[0] = ctx->Light.Light[ln].ConstantAttenuation; + value[1] = ctx->Light.Light[ln].LinearAttenuation; + value[2] = ctx->Light.Light[ln].QuadraticAttenuation; + value[3] = ctx->Light.Light[ln].SpotExponent; + return; + case STATE_SPOT_DIRECTION: + COPY_3V(value, ctx->Light.Light[ln].SpotDirection); + value[3] = ctx->Light.Light[ln]._CosCutoff; + return; + case STATE_SPOT_CUTOFF: + value[0] = ctx->Light.Light[ln].SpotCutoff; + return; + case STATE_HALF_VECTOR: + { + static const GLfloat eye_z[] = {0, 0, 1}; + GLfloat p[3]; + /* Compute infinite half angle vector: + * halfVector = normalize(normalize(lightPos) + (0, 0, 1)) + * light.EyePosition.w should be 0 for infinite lights. + */ + COPY_3V(p, ctx->Light.Light[ln].EyePosition); + NORMALIZE_3FV(p); + ADD_3V(value, p, eye_z); + NORMALIZE_3FV(value); + value[3] = 1.0; + } + return; + default: + _mesa_problem(ctx, "Invalid light state in fetch_state"); + return; + } + } + case STATE_LIGHTMODEL_AMBIENT: + COPY_4V(value, ctx->Light.Model.Ambient); + return; + case STATE_LIGHTMODEL_SCENECOLOR: + if (state[1] == 0) { + /* front */ + GLint i; + for (i = 0; i < 3; i++) { + value[i] = ctx->Light.Model.Ambient[i] + * ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_AMBIENT][i] + + ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_EMISSION][i]; + } + value[3] = ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_DIFFUSE][3]; + } + else { + /* back */ + GLint i; + for (i = 0; i < 3; i++) { + value[i] = ctx->Light.Model.Ambient[i] + * ctx->Light.Material.Attrib[MAT_ATTRIB_BACK_AMBIENT][i] + + ctx->Light.Material.Attrib[MAT_ATTRIB_BACK_EMISSION][i]; + } + value[3] = ctx->Light.Material.Attrib[MAT_ATTRIB_BACK_DIFFUSE][3]; + } + return; + case STATE_LIGHTPROD: + { + const GLuint ln = (GLuint) state[1]; + const GLuint face = (GLuint) state[2]; + GLint i; + ASSERT(face == 0 || face == 1); + switch (state[3]) { + case STATE_AMBIENT: + for (i = 0; i < 3; i++) { + value[i] = ctx->Light.Light[ln].Ambient[i] * + ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_AMBIENT+face][i]; + } + /* [3] = material alpha */ + value[3] = ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_AMBIENT+face][3]; + return; + case STATE_DIFFUSE: + for (i = 0; i < 3; i++) { + value[i] = ctx->Light.Light[ln].Diffuse[i] * + ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_DIFFUSE+face][i]; + } + /* [3] = material alpha */ + value[3] = ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_DIFFUSE+face][3]; + return; + case STATE_SPECULAR: + for (i = 0; i < 3; i++) { + value[i] = ctx->Light.Light[ln].Specular[i] * + ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_SPECULAR+face][i]; + } + /* [3] = material alpha */ + value[3] = ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_SPECULAR+face][3]; + return; + default: + _mesa_problem(ctx, "Invalid lightprod state in fetch_state"); + return; + } + } + case STATE_TEXGEN: + { + /* state[1] is the texture unit */ + const GLuint unit = (GLuint) state[1]; + /* state[2] is the texgen attribute */ + switch (state[2]) { + case STATE_TEXGEN_EYE_S: + COPY_4V(value, ctx->Texture.Unit[unit].GenS.EyePlane); + return; + case STATE_TEXGEN_EYE_T: + COPY_4V(value, ctx->Texture.Unit[unit].GenT.EyePlane); + return; + case STATE_TEXGEN_EYE_R: + COPY_4V(value, ctx->Texture.Unit[unit].GenR.EyePlane); + return; + case STATE_TEXGEN_EYE_Q: + COPY_4V(value, ctx->Texture.Unit[unit].GenQ.EyePlane); + return; + case STATE_TEXGEN_OBJECT_S: + COPY_4V(value, ctx->Texture.Unit[unit].GenS.ObjectPlane); + return; + case STATE_TEXGEN_OBJECT_T: + COPY_4V(value, ctx->Texture.Unit[unit].GenT.ObjectPlane); + return; + case STATE_TEXGEN_OBJECT_R: + COPY_4V(value, ctx->Texture.Unit[unit].GenR.ObjectPlane); + return; + case STATE_TEXGEN_OBJECT_Q: + COPY_4V(value, ctx->Texture.Unit[unit].GenQ.ObjectPlane); + return; + default: + _mesa_problem(ctx, "Invalid texgen state in fetch_state"); + return; + } + } + case STATE_TEXENV_COLOR: + { + /* state[1] is the texture unit */ + const GLuint unit = (GLuint) state[1]; + if(ctx->Color._ClampFragmentColor) + COPY_4V(value, ctx->Texture.Unit[unit].EnvColor); + else + COPY_4V(value, ctx->Texture.Unit[unit].EnvColorUnclamped); + } + return; + case STATE_FOG_COLOR: + if(ctx->Color._ClampFragmentColor) + COPY_4V(value, ctx->Fog.Color); + else + COPY_4V(value, ctx->Fog.ColorUnclamped); + return; + case STATE_FOG_PARAMS: + value[0] = ctx->Fog.Density; + value[1] = ctx->Fog.Start; + value[2] = ctx->Fog.End; + value[3] = (ctx->Fog.End == ctx->Fog.Start) + ? 1.0f : (GLfloat)(1.0 / (ctx->Fog.End - ctx->Fog.Start)); + return; + case STATE_CLIPPLANE: + { + const GLuint plane = (GLuint) state[1]; + COPY_4V(value, ctx->Transform.EyeUserPlane[plane]); + } + return; + case STATE_POINT_SIZE: + value[0] = ctx->Point.Size; + value[1] = ctx->Point.MinSize; + value[2] = ctx->Point.MaxSize; + value[3] = ctx->Point.Threshold; + return; + case STATE_POINT_ATTENUATION: + value[0] = ctx->Point.Params[0]; + value[1] = ctx->Point.Params[1]; + value[2] = ctx->Point.Params[2]; + value[3] = 1.0F; + return; + case STATE_MODELVIEW_MATRIX: + case STATE_PROJECTION_MATRIX: + case STATE_MVP_MATRIX: + case STATE_TEXTURE_MATRIX: + case STATE_PROGRAM_MATRIX: + { + /* state[0] = modelview, projection, texture, etc. */ + /* state[1] = which texture matrix or program matrix */ + /* state[2] = first row to fetch */ + /* state[3] = last row to fetch */ + /* state[4] = transpose, inverse or invtrans */ + const GLmatrix *matrix; + const gl_state_index mat = state[0]; + const GLuint index = (GLuint) state[1]; + const GLuint firstRow = (GLuint) state[2]; + const GLuint lastRow = (GLuint) state[3]; + const gl_state_index modifier = state[4]; + const GLfloat *m; + GLuint row, i; + ASSERT(firstRow >= 0); + ASSERT(firstRow < 4); + ASSERT(lastRow >= 0); + ASSERT(lastRow < 4); + if (mat == STATE_MODELVIEW_MATRIX) { + matrix = ctx->ModelviewMatrixStack.Top; + } + else if (mat == STATE_PROJECTION_MATRIX) { + matrix = ctx->ProjectionMatrixStack.Top; + } + else if (mat == STATE_MVP_MATRIX) { + matrix = &ctx->_ModelProjectMatrix; + } + else if (mat == STATE_TEXTURE_MATRIX) { + ASSERT(index < Elements(ctx->TextureMatrixStack)); + matrix = ctx->TextureMatrixStack[index].Top; + } + else if (mat == STATE_PROGRAM_MATRIX) { + ASSERT(index < Elements(ctx->ProgramMatrixStack)); + matrix = ctx->ProgramMatrixStack[index].Top; + } + else { + _mesa_problem(ctx, "Bad matrix name in _mesa_fetch_state()"); + return; + } + if (modifier == STATE_MATRIX_INVERSE || + modifier == STATE_MATRIX_INVTRANS) { + /* Be sure inverse is up to date: + */ + _math_matrix_alloc_inv( (GLmatrix *) matrix ); + _math_matrix_analyse( (GLmatrix*) matrix ); + m = matrix->inv; + } + else { + m = matrix->m; + } + if (modifier == STATE_MATRIX_TRANSPOSE || + modifier == STATE_MATRIX_INVTRANS) { + for (i = 0, row = firstRow; row <= lastRow; row++) { + value[i++] = m[row * 4 + 0]; + value[i++] = m[row * 4 + 1]; + value[i++] = m[row * 4 + 2]; + value[i++] = m[row * 4 + 3]; + } + } + else { + for (i = 0, row = firstRow; row <= lastRow; row++) { + value[i++] = m[row + 0]; + value[i++] = m[row + 4]; + value[i++] = m[row + 8]; + value[i++] = m[row + 12]; + } + } + } + return; + case STATE_DEPTH_RANGE: + value[0] = ctx->Viewport.Near; /* near */ + value[1] = ctx->Viewport.Far; /* far */ + value[2] = ctx->Viewport.Far - ctx->Viewport.Near; /* far - near */ + value[3] = 1.0; + return; + case STATE_FRAGMENT_PROGRAM: + { + /* state[1] = {STATE_ENV, STATE_LOCAL} */ + /* state[2] = parameter index */ + const int idx = (int) state[2]; + switch (state[1]) { + case STATE_ENV: + COPY_4V(value, ctx->FragmentProgram.Parameters[idx]); + return; + case STATE_LOCAL: + COPY_4V(value, ctx->FragmentProgram.Current->Base.LocalParams[idx]); + return; + default: + _mesa_problem(ctx, "Bad state switch in _mesa_fetch_state()"); + return; + } + } + return; + + case STATE_VERTEX_PROGRAM: + { + /* state[1] = {STATE_ENV, STATE_LOCAL} */ + /* state[2] = parameter index */ + const int idx = (int) state[2]; + switch (state[1]) { + case STATE_ENV: + COPY_4V(value, ctx->VertexProgram.Parameters[idx]); + return; + case STATE_LOCAL: + COPY_4V(value, ctx->VertexProgram.Current->Base.LocalParams[idx]); + return; + default: + _mesa_problem(ctx, "Bad state switch in _mesa_fetch_state()"); + return; + } + } + return; + + case STATE_NORMAL_SCALE: + ASSIGN_4V(value, ctx->_ModelViewInvScale, 0, 0, 1); + return; + + case STATE_INTERNAL: + switch (state[1]) { + case STATE_CURRENT_ATTRIB: + { + const GLuint idx = (GLuint) state[2]; + COPY_4V(value, ctx->Current.Attrib[idx]); + } + return; + + case STATE_CURRENT_ATTRIB_MAYBE_VP_CLAMPED: + { + const GLuint idx = (GLuint) state[2]; + if(ctx->Light._ClampVertexColor && + (idx == VERT_ATTRIB_COLOR0 || + idx == VERT_ATTRIB_COLOR1)) { + value[0] = CLAMP(ctx->Current.Attrib[idx][0], 0.0f, 1.0f); + value[1] = CLAMP(ctx->Current.Attrib[idx][1], 0.0f, 1.0f); + value[2] = CLAMP(ctx->Current.Attrib[idx][2], 0.0f, 1.0f); + value[3] = CLAMP(ctx->Current.Attrib[idx][3], 0.0f, 1.0f); + } + else + COPY_4V(value, ctx->Current.Attrib[idx]); + } + return; + + case STATE_NORMAL_SCALE: + ASSIGN_4V(value, + ctx->_ModelViewInvScale, + ctx->_ModelViewInvScale, + ctx->_ModelViewInvScale, + 1); + return; + + case STATE_TEXRECT_SCALE: + /* Value = { 1/texWidth, 1/texHeight, 0, 1 }. + * Used to convert unnormalized texcoords to normalized texcoords. + */ + { + const int unit = (int) state[2]; + const struct gl_texture_object *texObj + = ctx->Texture.Unit[unit]._Current; + if (texObj) { + struct gl_texture_image *texImage = texObj->Image[0][0]; + ASSIGN_4V(value, + (GLfloat) (1.0 / texImage->Width), + (GLfloat) (1.0 / texImage->Height), + 0.0f, 1.0f); + } + } + return; + + case STATE_FOG_PARAMS_OPTIMIZED: + /* for simpler per-vertex/pixel fog calcs. POW (for EXP/EXP2 fog) + * might be more expensive than EX2 on some hw, plus it needs + * another constant (e) anyway. Linear fog can now be done with a + * single MAD. + * linear: fogcoord * -1/(end-start) + end/(end-start) + * exp: 2^-(density/ln(2) * fogcoord) + * exp2: 2^-((density/(ln(2)^2) * fogcoord)^2) + */ + value[0] = (ctx->Fog.End == ctx->Fog.Start) + ? 1.0f : (GLfloat)(-1.0F / (ctx->Fog.End - ctx->Fog.Start)); + value[1] = ctx->Fog.End * -value[0]; + value[2] = (GLfloat)(ctx->Fog.Density * ONE_DIV_LN2); + value[3] = (GLfloat)(ctx->Fog.Density * ONE_DIV_SQRT_LN2); + return; + + case STATE_POINT_SIZE_CLAMPED: + { + /* this includes implementation dependent limits, to avoid + * another potentially necessary clamp. + * Note: for sprites, point smooth (point AA) is ignored + * and we'll clamp to MinPointSizeAA and MaxPointSize, because we + * expect drivers will want to say their minimum for AA size is 0.0 + * but for non-AA it's 1.0 (because normal points with size below 1.0 + * need to get rounded up to 1.0, hence never disappear). GL does + * not specify max clamp size for sprites, other than it needs to be + * at least as large as max AA size, hence use non-AA size there. + */ + GLfloat minImplSize; + GLfloat maxImplSize; + if (ctx->Point.PointSprite) { + minImplSize = ctx->Const.MinPointSizeAA; + maxImplSize = ctx->Const.MaxPointSize; + } + else if (ctx->Point.SmoothFlag || ctx->Multisample._Enabled) { + minImplSize = ctx->Const.MinPointSizeAA; + maxImplSize = ctx->Const.MaxPointSizeAA; + } + else { + minImplSize = ctx->Const.MinPointSize; + maxImplSize = ctx->Const.MaxPointSize; + } + value[0] = ctx->Point.Size; + value[1] = ctx->Point.MinSize >= minImplSize ? ctx->Point.MinSize : minImplSize; + value[2] = ctx->Point.MaxSize <= maxImplSize ? ctx->Point.MaxSize : maxImplSize; + value[3] = ctx->Point.Threshold; + } + return; + case STATE_POINT_SIZE_IMPL_CLAMP: + { + /* for implementation clamp only in vs */ + GLfloat minImplSize; + GLfloat maxImplSize; + if (ctx->Point.PointSprite) { + minImplSize = ctx->Const.MinPointSizeAA; + maxImplSize = ctx->Const.MaxPointSize; + } + else if (ctx->Point.SmoothFlag || ctx->Multisample._Enabled) { + minImplSize = ctx->Const.MinPointSizeAA; + maxImplSize = ctx->Const.MaxPointSizeAA; + } + else { + minImplSize = ctx->Const.MinPointSize; + maxImplSize = ctx->Const.MaxPointSize; + } + value[0] = ctx->Point.Size; + value[1] = minImplSize; + value[2] = maxImplSize; + value[3] = ctx->Point.Threshold; + } + return; + case STATE_LIGHT_SPOT_DIR_NORMALIZED: + { + /* here, state[2] is the light number */ + /* pre-normalize spot dir */ + const GLuint ln = (GLuint) state[2]; + COPY_3V(value, ctx->Light.Light[ln]._NormSpotDirection); + value[3] = ctx->Light.Light[ln]._CosCutoff; + } + return; + + case STATE_LIGHT_POSITION: + { + const GLuint ln = (GLuint) state[2]; + COPY_4V(value, ctx->Light.Light[ln]._Position); + } + return; + + case STATE_LIGHT_POSITION_NORMALIZED: + { + const GLuint ln = (GLuint) state[2]; + COPY_4V(value, ctx->Light.Light[ln]._Position); + NORMALIZE_3FV( value ); + } + return; + + case STATE_LIGHT_HALF_VECTOR: + { + const GLuint ln = (GLuint) state[2]; + GLfloat p[3]; + /* Compute infinite half angle vector: + * halfVector = normalize(normalize(lightPos) + (0, 0, 1)) + * light.EyePosition.w should be 0 for infinite lights. + */ + COPY_3V(p, ctx->Light.Light[ln]._Position); + NORMALIZE_3FV(p); + ADD_3V(value, p, ctx->_EyeZDir); + NORMALIZE_3FV(value); + value[3] = 1.0; + } + return; + + case STATE_PT_SCALE: + value[0] = ctx->Pixel.RedScale; + value[1] = ctx->Pixel.GreenScale; + value[2] = ctx->Pixel.BlueScale; + value[3] = ctx->Pixel.AlphaScale; + return; + + case STATE_PT_BIAS: + value[0] = ctx->Pixel.RedBias; + value[1] = ctx->Pixel.GreenBias; + value[2] = ctx->Pixel.BlueBias; + value[3] = ctx->Pixel.AlphaBias; + return; + + case STATE_SHADOW_AMBIENT: + { + const int unit = (int) state[2]; + const struct gl_texture_object *texObj + = ctx->Texture.Unit[unit]._Current; + if (texObj) { + value[0] = + value[1] = + value[2] = + value[3] = texObj->CompareFailValue; + } + } + return; + + case STATE_FB_SIZE: + value[0] = (GLfloat) (ctx->DrawBuffer->Width - 1); + value[1] = (GLfloat) (ctx->DrawBuffer->Height - 1); + value[2] = 0.0F; + value[3] = 0.0F; + return; + + case STATE_FB_WPOS_Y_TRANSFORM: + /* A driver may negate this conditional by using ZW swizzle + * instead of XY (based on e.g. some other state). */ + if (ctx->DrawBuffer->Name != 0) { + /* Identity (XY) followed by flipping Y upside down (ZW). */ + value[0] = 1.0F; + value[1] = 0.0F; + value[2] = -1.0F; + value[3] = (GLfloat) (ctx->DrawBuffer->Height - 1); + } else { + /* Flipping Y upside down (XY) followed by identity (ZW). */ + value[0] = -1.0F; + value[1] = (GLfloat) (ctx->DrawBuffer->Height - 1); + value[2] = 1.0F; + value[3] = 0.0F; + } + return; + + case STATE_ROT_MATRIX_0: + { + const int unit = (int) state[2]; + GLfloat *rotMat22 = ctx->Texture.Unit[unit].RotMatrix; + value[0] = rotMat22[0]; + value[1] = rotMat22[2]; + value[2] = 0.0; + value[3] = 0.0; + } + return; + + case STATE_ROT_MATRIX_1: + { + const int unit = (int) state[2]; + GLfloat *rotMat22 = ctx->Texture.Unit[unit].RotMatrix; + value[0] = rotMat22[1]; + value[1] = rotMat22[3]; + value[2] = 0.0; + value[3] = 0.0; + } + return; + + /* XXX: make sure new tokens added here are also handled in the + * _mesa_program_state_flags() switch, below. + */ + default: + /* Unknown state indexes are silently ignored here. + * Drivers may do something special. + */ + return; + } + return; + + default: + _mesa_problem(ctx, "Invalid state in _mesa_fetch_state"); + return; + } +} + + +/** + * Return a bitmask of the Mesa state flags (_NEW_* values) which would + * indicate that the given context state may have changed. + * The bitmask is used during validation to determine if we need to update + * vertex/fragment program parameters (like "state.material.color") when + * some GL state has changed. + */ +GLbitfield +_mesa_program_state_flags(const gl_state_index state[STATE_LENGTH]) +{ + switch (state[0]) { + case STATE_MATERIAL: + case STATE_LIGHT: + case STATE_LIGHTMODEL_AMBIENT: + case STATE_LIGHTMODEL_SCENECOLOR: + case STATE_LIGHTPROD: + return _NEW_LIGHT; + + case STATE_TEXGEN: + return _NEW_TEXTURE; + case STATE_TEXENV_COLOR: + return _NEW_TEXTURE | _NEW_BUFFERS | _NEW_FRAG_CLAMP; + + case STATE_FOG_COLOR: + return _NEW_FOG | _NEW_BUFFERS | _NEW_FRAG_CLAMP; + case STATE_FOG_PARAMS: + return _NEW_FOG; + + case STATE_CLIPPLANE: + return _NEW_TRANSFORM; + + case STATE_POINT_SIZE: + case STATE_POINT_ATTENUATION: + return _NEW_POINT; + + case STATE_MODELVIEW_MATRIX: + return _NEW_MODELVIEW; + case STATE_PROJECTION_MATRIX: + return _NEW_PROJECTION; + case STATE_MVP_MATRIX: + return _NEW_MODELVIEW | _NEW_PROJECTION; + case STATE_TEXTURE_MATRIX: + return _NEW_TEXTURE_MATRIX; + case STATE_PROGRAM_MATRIX: + return _NEW_TRACK_MATRIX; + + case STATE_DEPTH_RANGE: + return _NEW_VIEWPORT; + + case STATE_FRAGMENT_PROGRAM: + case STATE_VERTEX_PROGRAM: + return _NEW_PROGRAM; + + case STATE_NORMAL_SCALE: + return _NEW_MODELVIEW; + + case STATE_INTERNAL: + switch (state[1]) { + case STATE_CURRENT_ATTRIB: + return _NEW_CURRENT_ATTRIB; + case STATE_CURRENT_ATTRIB_MAYBE_VP_CLAMPED: + return _NEW_CURRENT_ATTRIB | _NEW_LIGHT | _NEW_BUFFERS; + + case STATE_NORMAL_SCALE: + return _NEW_MODELVIEW; + + case STATE_TEXRECT_SCALE: + case STATE_SHADOW_AMBIENT: + case STATE_ROT_MATRIX_0: + case STATE_ROT_MATRIX_1: + return _NEW_TEXTURE; + case STATE_FOG_PARAMS_OPTIMIZED: + return _NEW_FOG; + case STATE_POINT_SIZE_CLAMPED: + case STATE_POINT_SIZE_IMPL_CLAMP: + return _NEW_POINT | _NEW_MULTISAMPLE; + case STATE_LIGHT_SPOT_DIR_NORMALIZED: + case STATE_LIGHT_POSITION: + case STATE_LIGHT_POSITION_NORMALIZED: + case STATE_LIGHT_HALF_VECTOR: + return _NEW_LIGHT; + + case STATE_PT_SCALE: + case STATE_PT_BIAS: + return _NEW_PIXEL; + + case STATE_FB_SIZE: + case STATE_FB_WPOS_Y_TRANSFORM: + return _NEW_BUFFERS; + + default: + /* unknown state indexes are silently ignored and + * no flag set, since it is handled by the driver. + */ + return 0; + } + + default: + _mesa_problem(NULL, "unexpected state[0] in make_state_flags()"); + return 0; + } +} + + +static void +append(char *dst, const char *src) +{ + while (*dst) + dst++; + while (*src) + *dst++ = *src++; + *dst = 0; +} + + +/** + * Convert token 'k' to a string, append it onto 'dst' string. + */ +static void +append_token(char *dst, gl_state_index k) +{ + switch (k) { + case STATE_MATERIAL: + append(dst, "material"); + break; + case STATE_LIGHT: + append(dst, "light"); + break; + case STATE_LIGHTMODEL_AMBIENT: + append(dst, "lightmodel.ambient"); + break; + case STATE_LIGHTMODEL_SCENECOLOR: + break; + case STATE_LIGHTPROD: + append(dst, "lightprod"); + break; + case STATE_TEXGEN: + append(dst, "texgen"); + break; + case STATE_FOG_COLOR: + append(dst, "fog.color"); + break; + case STATE_FOG_PARAMS: + append(dst, "fog.params"); + break; + case STATE_CLIPPLANE: + append(dst, "clip"); + break; + case STATE_POINT_SIZE: + append(dst, "point.size"); + break; + case STATE_POINT_ATTENUATION: + append(dst, "point.attenuation"); + break; + case STATE_MODELVIEW_MATRIX: + append(dst, "matrix.modelview"); + break; + case STATE_PROJECTION_MATRIX: + append(dst, "matrix.projection"); + break; + case STATE_MVP_MATRIX: + append(dst, "matrix.mvp"); + break; + case STATE_TEXTURE_MATRIX: + append(dst, "matrix.texture"); + break; + case STATE_PROGRAM_MATRIX: + append(dst, "matrix.program"); + break; + case STATE_MATRIX_INVERSE: + append(dst, ".inverse"); + break; + case STATE_MATRIX_TRANSPOSE: + append(dst, ".transpose"); + break; + case STATE_MATRIX_INVTRANS: + append(dst, ".invtrans"); + break; + case STATE_AMBIENT: + append(dst, ".ambient"); + break; + case STATE_DIFFUSE: + append(dst, ".diffuse"); + break; + case STATE_SPECULAR: + append(dst, ".specular"); + break; + case STATE_EMISSION: + append(dst, ".emission"); + break; + case STATE_SHININESS: + append(dst, "lshininess"); + break; + case STATE_HALF_VECTOR: + append(dst, ".half"); + break; + case STATE_POSITION: + append(dst, ".position"); + break; + case STATE_ATTENUATION: + append(dst, ".attenuation"); + break; + case STATE_SPOT_DIRECTION: + append(dst, ".spot.direction"); + break; + case STATE_SPOT_CUTOFF: + append(dst, ".spot.cutoff"); + break; + case STATE_TEXGEN_EYE_S: + append(dst, ".eye.s"); + break; + case STATE_TEXGEN_EYE_T: + append(dst, ".eye.t"); + break; + case STATE_TEXGEN_EYE_R: + append(dst, ".eye.r"); + break; + case STATE_TEXGEN_EYE_Q: + append(dst, ".eye.q"); + break; + case STATE_TEXGEN_OBJECT_S: + append(dst, ".object.s"); + break; + case STATE_TEXGEN_OBJECT_T: + append(dst, ".object.t"); + break; + case STATE_TEXGEN_OBJECT_R: + append(dst, ".object.r"); + break; + case STATE_TEXGEN_OBJECT_Q: + append(dst, ".object.q"); + break; + case STATE_TEXENV_COLOR: + append(dst, "texenv"); + break; + case STATE_DEPTH_RANGE: + append(dst, "depth.range"); + break; + case STATE_VERTEX_PROGRAM: + case STATE_FRAGMENT_PROGRAM: + break; + case STATE_ENV: + append(dst, "env"); + break; + case STATE_LOCAL: + append(dst, "local"); + break; + /* BEGIN internal state vars */ + case STATE_INTERNAL: + append(dst, ".internal."); + break; + case STATE_CURRENT_ATTRIB: + append(dst, "current"); + break; + case STATE_NORMAL_SCALE: + append(dst, "normalScale"); + break; + case STATE_TEXRECT_SCALE: + append(dst, "texrectScale"); + break; + case STATE_FOG_PARAMS_OPTIMIZED: + append(dst, "fogParamsOptimized"); + break; + case STATE_POINT_SIZE_CLAMPED: + append(dst, "pointSizeClamped"); + break; + case STATE_POINT_SIZE_IMPL_CLAMP: + append(dst, "pointSizeImplClamp"); + break; + case STATE_LIGHT_SPOT_DIR_NORMALIZED: + append(dst, "lightSpotDirNormalized"); + break; + case STATE_LIGHT_POSITION: + append(dst, "lightPosition"); + break; + case STATE_LIGHT_POSITION_NORMALIZED: + append(dst, "light.position.normalized"); + break; + case STATE_LIGHT_HALF_VECTOR: + append(dst, "lightHalfVector"); + break; + case STATE_PT_SCALE: + append(dst, "PTscale"); + break; + case STATE_PT_BIAS: + append(dst, "PTbias"); + break; + case STATE_SHADOW_AMBIENT: + append(dst, "CompareFailValue"); + break; + case STATE_FB_SIZE: + append(dst, "FbSize"); + break; + case STATE_FB_WPOS_Y_TRANSFORM: + append(dst, "FbWposYTransform"); + break; + case STATE_ROT_MATRIX_0: + append(dst, "rotMatrixRow0"); + break; + case STATE_ROT_MATRIX_1: + append(dst, "rotMatrixRow1"); + break; + default: + /* probably STATE_INTERNAL_DRIVER+i (driver private state) */ + append(dst, "driverState"); + } +} + +static void +append_face(char *dst, GLint face) +{ + if (face == 0) + append(dst, "front."); + else + append(dst, "back."); +} + +static void +append_index(char *dst, GLint index) +{ + char s[20]; + sprintf(s, "[%d]", index); + append(dst, s); +} + +/** + * Make a string from the given state vector. + * For example, return "state.matrix.texture[2].inverse". + * Use free() to deallocate the string. + */ +char * +_mesa_program_state_string(const gl_state_index state[STATE_LENGTH]) +{ + char str[1000] = ""; + char tmp[30]; + + append(str, "state."); + append_token(str, state[0]); + + switch (state[0]) { + case STATE_MATERIAL: + append_face(str, state[1]); + append_token(str, state[2]); + break; + case STATE_LIGHT: + append_index(str, state[1]); /* light number [i]. */ + append_token(str, state[2]); /* coefficients */ + break; + case STATE_LIGHTMODEL_AMBIENT: + append(str, "lightmodel.ambient"); + break; + case STATE_LIGHTMODEL_SCENECOLOR: + if (state[1] == 0) { + append(str, "lightmodel.front.scenecolor"); + } + else { + append(str, "lightmodel.back.scenecolor"); + } + break; + case STATE_LIGHTPROD: + append_index(str, state[1]); /* light number [i]. */ + append_face(str, state[2]); + append_token(str, state[3]); + break; + case STATE_TEXGEN: + append_index(str, state[1]); /* tex unit [i] */ + append_token(str, state[2]); /* plane coef */ + break; + case STATE_TEXENV_COLOR: + append_index(str, state[1]); /* tex unit [i] */ + append(str, "color"); + break; + case STATE_CLIPPLANE: + append_index(str, state[1]); /* plane [i] */ + append(str, ".plane"); + break; + case STATE_MODELVIEW_MATRIX: + case STATE_PROJECTION_MATRIX: + case STATE_MVP_MATRIX: + case STATE_TEXTURE_MATRIX: + case STATE_PROGRAM_MATRIX: + { + /* state[0] = modelview, projection, texture, etc. */ + /* state[1] = which texture matrix or program matrix */ + /* state[2] = first row to fetch */ + /* state[3] = last row to fetch */ + /* state[4] = transpose, inverse or invtrans */ + const gl_state_index mat = state[0]; + const GLuint index = (GLuint) state[1]; + const GLuint firstRow = (GLuint) state[2]; + const GLuint lastRow = (GLuint) state[3]; + const gl_state_index modifier = state[4]; + if (index || + mat == STATE_TEXTURE_MATRIX || + mat == STATE_PROGRAM_MATRIX) + append_index(str, index); + if (modifier) + append_token(str, modifier); + if (firstRow == lastRow) + sprintf(tmp, ".row[%d]", firstRow); + else + sprintf(tmp, ".row[%d..%d]", firstRow, lastRow); + append(str, tmp); + } + break; + case STATE_POINT_SIZE: + break; + case STATE_POINT_ATTENUATION: + break; + case STATE_FOG_PARAMS: + break; + case STATE_FOG_COLOR: + break; + case STATE_DEPTH_RANGE: + break; + case STATE_FRAGMENT_PROGRAM: + case STATE_VERTEX_PROGRAM: + /* state[1] = {STATE_ENV, STATE_LOCAL} */ + /* state[2] = parameter index */ + append_token(str, state[1]); + append_index(str, state[2]); + break; + case STATE_NORMAL_SCALE: + break; + case STATE_INTERNAL: + append_token(str, state[1]); + if (state[1] == STATE_CURRENT_ATTRIB) + append_index(str, state[2]); + break; + default: + _mesa_problem(NULL, "Invalid state in _mesa_program_state_string"); + break; + } + + return _mesa_strdup(str); +} + + +/** + * Loop over all the parameters in a parameter list. If the parameter + * is a GL state reference, look up the current value of that state + * variable and put it into the parameter's Value[4] array. + * Other parameter types never change or are explicitly set by the user + * with glUniform() or glProgramParameter(), etc. + * This would be called at glBegin time. + */ +void +_mesa_load_state_parameters(struct gl_context *ctx, + struct gl_program_parameter_list *paramList) +{ + GLuint i; + + if (!paramList) + return; + + for (i = 0; i < paramList->NumParameters; i++) { + if (paramList->Parameters[i].Type == PROGRAM_STATE_VAR) { + _mesa_fetch_state(ctx, + paramList->Parameters[i].StateIndexes, + paramList->ParameterValues[i]); + } + } +} + + +/** + * Copy the 16 elements of a matrix into four consecutive program + * registers starting at 'pos'. + */ +static void +load_matrix(GLfloat registers[][4], GLuint pos, const GLfloat mat[16]) +{ + GLuint i; + for (i = 0; i < 4; i++) { + registers[pos + i][0] = mat[0 + i]; + registers[pos + i][1] = mat[4 + i]; + registers[pos + i][2] = mat[8 + i]; + registers[pos + i][3] = mat[12 + i]; + } +} + + +/** + * As above, but transpose the matrix. + */ +static void +load_transpose_matrix(GLfloat registers[][4], GLuint pos, + const GLfloat mat[16]) +{ + memcpy(registers[pos], mat, 16 * sizeof(GLfloat)); +} + + +/** + * Load current vertex program's parameter registers with tracked + * matrices (if NV program). This only needs to be done per + * glBegin/glEnd, not per-vertex. + */ +void +_mesa_load_tracked_matrices(struct gl_context *ctx) +{ + GLuint i; + + for (i = 0; i < MAX_NV_VERTEX_PROGRAM_PARAMS / 4; i++) { + /* point 'mat' at source matrix */ + GLmatrix *mat; + if (ctx->VertexProgram.TrackMatrix[i] == GL_MODELVIEW) { + mat = ctx->ModelviewMatrixStack.Top; + } + else if (ctx->VertexProgram.TrackMatrix[i] == GL_PROJECTION) { + mat = ctx->ProjectionMatrixStack.Top; + } + else if (ctx->VertexProgram.TrackMatrix[i] == GL_TEXTURE) { + GLuint unit = MIN2(ctx->Texture.CurrentUnit, + Elements(ctx->TextureMatrixStack) - 1); + mat = ctx->TextureMatrixStack[unit].Top; + } + else if (ctx->VertexProgram.TrackMatrix[i]==GL_MODELVIEW_PROJECTION_NV) { + /* XXX verify the combined matrix is up to date */ + mat = &ctx->_ModelProjectMatrix; + } + else if (ctx->VertexProgram.TrackMatrix[i] >= GL_MATRIX0_NV && + ctx->VertexProgram.TrackMatrix[i] <= GL_MATRIX7_NV) { + GLuint n = ctx->VertexProgram.TrackMatrix[i] - GL_MATRIX0_NV; + ASSERT(n < Elements(ctx->ProgramMatrixStack)); + mat = ctx->ProgramMatrixStack[n].Top; + } + else { + /* no matrix is tracked, but we leave the register values as-is */ + assert(ctx->VertexProgram.TrackMatrix[i] == GL_NONE); + continue; + } + + /* load the matrix values into sequential registers */ + if (ctx->VertexProgram.TrackMatrixTransform[i] == GL_IDENTITY_NV) { + load_matrix(ctx->VertexProgram.Parameters, i*4, mat->m); + } + else if (ctx->VertexProgram.TrackMatrixTransform[i] == GL_INVERSE_NV) { + _math_matrix_analyse(mat); /* update the inverse */ + ASSERT(!_math_matrix_is_dirty(mat)); + load_matrix(ctx->VertexProgram.Parameters, i*4, mat->inv); + } + else if (ctx->VertexProgram.TrackMatrixTransform[i] == GL_TRANSPOSE_NV) { + load_transpose_matrix(ctx->VertexProgram.Parameters, i*4, mat->m); + } + else { + assert(ctx->VertexProgram.TrackMatrixTransform[i] + == GL_INVERSE_TRANSPOSE_NV); + _math_matrix_analyse(mat); /* update the inverse */ + ASSERT(!_math_matrix_is_dirty(mat)); + load_transpose_matrix(ctx->VertexProgram.Parameters, i*4, mat->inv); + } + } +} diff --git a/mesalib/src/mesa/program/prog_statevars.h b/mesalib/src/mesa/program/prog_statevars.h index a92b77855..9fe8d81b3 100644 --- a/mesalib/src/mesa/program/prog_statevars.h +++ b/mesalib/src/mesa/program/prog_statevars.h @@ -1,147 +1,148 @@ -/* - * Mesa 3-D graphics library - * Version: 7.1 - * - * Copyright (C) 1999-2007 Brian Paul All Rights Reserved. - * - * 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 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 - * BRIAN PAUL 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. - */ - -#ifndef PROG_STATEVARS_H -#define PROG_STATEVARS_H - -#include "main/glheader.h" - -struct gl_context; -struct gl_program_parameter_list; - -/** - * Number of STATE_* values we need to address any GL state. - * Used to dimension arrays. - */ -#define STATE_LENGTH 5 - - -/** - * Used for describing GL state referenced from inside ARB vertex and - * fragment programs. - * A string such as "state.light[0].ambient" gets translated into a - * sequence of tokens such as [ STATE_LIGHT, 0, STATE_AMBIENT ]. - * - * For state that's an array, like STATE_CLIPPLANE, the 2nd token [1] should - * always be the array index. - */ -typedef enum gl_state_index_ { - STATE_MATERIAL = 100, /* start at 100 so small ints are seen as ints */ - - STATE_LIGHT, - STATE_LIGHTMODEL_AMBIENT, - STATE_LIGHTMODEL_SCENECOLOR, - STATE_LIGHTPROD, - - STATE_TEXGEN, - - STATE_FOG_COLOR, - STATE_FOG_PARAMS, - - STATE_CLIPPLANE, - - STATE_POINT_SIZE, - STATE_POINT_ATTENUATION, - - STATE_MODELVIEW_MATRIX, - STATE_PROJECTION_MATRIX, - STATE_MVP_MATRIX, - STATE_TEXTURE_MATRIX, - STATE_PROGRAM_MATRIX, - STATE_MATRIX_INVERSE, - STATE_MATRIX_TRANSPOSE, - STATE_MATRIX_INVTRANS, - - STATE_AMBIENT, - STATE_DIFFUSE, - STATE_SPECULAR, - STATE_EMISSION, - STATE_SHININESS, - STATE_HALF_VECTOR, - - STATE_POSITION, /**< xyzw = position */ - STATE_ATTENUATION, /**< xyz = attenuation, w = spot exponent */ - STATE_SPOT_DIRECTION, /**< xyz = direction, w = cos(cutoff) */ - STATE_SPOT_CUTOFF, /**< x = cutoff, yzw = undefined */ - - STATE_TEXGEN_EYE_S, - STATE_TEXGEN_EYE_T, - STATE_TEXGEN_EYE_R, - STATE_TEXGEN_EYE_Q, - STATE_TEXGEN_OBJECT_S, - STATE_TEXGEN_OBJECT_T, - STATE_TEXGEN_OBJECT_R, - STATE_TEXGEN_OBJECT_Q, - - STATE_TEXENV_COLOR, - - STATE_DEPTH_RANGE, - - STATE_VERTEX_PROGRAM, - STATE_FRAGMENT_PROGRAM, - - STATE_ENV, - STATE_LOCAL, - - STATE_INTERNAL, /* Mesa additions */ - STATE_CURRENT_ATTRIB, /* ctx->Current vertex attrib value */ - STATE_NORMAL_SCALE, - STATE_TEXRECT_SCALE, - STATE_FOG_PARAMS_OPTIMIZED, /* for faster fog calc */ - STATE_POINT_SIZE_CLAMPED, /* includes implementation dependent size clamp */ - STATE_POINT_SIZE_IMPL_CLAMP, /* for implementation clamp only in vs */ - STATE_LIGHT_SPOT_DIR_NORMALIZED, /* pre-normalized spot dir */ - STATE_LIGHT_POSITION, /* object vs eye space */ - STATE_LIGHT_POSITION_NORMALIZED, /* object vs eye space */ - STATE_LIGHT_HALF_VECTOR, /* object vs eye space */ - STATE_PT_SCALE, /**< Pixel transfer RGBA scale */ - STATE_PT_BIAS, /**< Pixel transfer RGBA bias */ - STATE_SHADOW_AMBIENT, /**< ARB_shadow_ambient fail value; token[2] is texture unit index */ - STATE_FB_SIZE, /**< (width-1, height-1, 0, 0) */ - STATE_FB_WPOS_Y_TRANSFORM, /**< (1, 0, -1, height-1) if a FBO is bound, (-1, height-1, 1, 0) otherwise */ - STATE_ROT_MATRIX_0, /**< ATI_envmap_bumpmap, rot matrix row 0 */ - STATE_ROT_MATRIX_1, /**< ATI_envmap_bumpmap, rot matrix row 1 */ - STATE_INTERNAL_DRIVER /* first available state index for drivers (must be last) */ -} gl_state_index; - - - -extern void -_mesa_load_state_parameters(struct gl_context *ctx, - struct gl_program_parameter_list *paramList); - - -extern GLbitfield -_mesa_program_state_flags(const gl_state_index state[STATE_LENGTH]); - - -extern char * -_mesa_program_state_string(const gl_state_index state[STATE_LENGTH]); - - -extern void -_mesa_load_tracked_matrices(struct gl_context *ctx); - - -#endif /* PROG_STATEVARS_H */ +/* + * Mesa 3-D graphics library + * Version: 7.1 + * + * Copyright (C) 1999-2007 Brian Paul All Rights Reserved. + * + * 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 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 + * BRIAN PAUL 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. + */ + +#ifndef PROG_STATEVARS_H +#define PROG_STATEVARS_H + +#include "main/glheader.h" + +struct gl_context; +struct gl_program_parameter_list; + +/** + * Number of STATE_* values we need to address any GL state. + * Used to dimension arrays. + */ +#define STATE_LENGTH 5 + + +/** + * Used for describing GL state referenced from inside ARB vertex and + * fragment programs. + * A string such as "state.light[0].ambient" gets translated into a + * sequence of tokens such as [ STATE_LIGHT, 0, STATE_AMBIENT ]. + * + * For state that's an array, like STATE_CLIPPLANE, the 2nd token [1] should + * always be the array index. + */ +typedef enum gl_state_index_ { + STATE_MATERIAL = 100, /* start at 100 so small ints are seen as ints */ + + STATE_LIGHT, + STATE_LIGHTMODEL_AMBIENT, + STATE_LIGHTMODEL_SCENECOLOR, + STATE_LIGHTPROD, + + STATE_TEXGEN, + + STATE_FOG_COLOR, + STATE_FOG_PARAMS, + + STATE_CLIPPLANE, + + STATE_POINT_SIZE, + STATE_POINT_ATTENUATION, + + STATE_MODELVIEW_MATRIX, + STATE_PROJECTION_MATRIX, + STATE_MVP_MATRIX, + STATE_TEXTURE_MATRIX, + STATE_PROGRAM_MATRIX, + STATE_MATRIX_INVERSE, + STATE_MATRIX_TRANSPOSE, + STATE_MATRIX_INVTRANS, + + STATE_AMBIENT, + STATE_DIFFUSE, + STATE_SPECULAR, + STATE_EMISSION, + STATE_SHININESS, + STATE_HALF_VECTOR, + + STATE_POSITION, /**< xyzw = position */ + STATE_ATTENUATION, /**< xyz = attenuation, w = spot exponent */ + STATE_SPOT_DIRECTION, /**< xyz = direction, w = cos(cutoff) */ + STATE_SPOT_CUTOFF, /**< x = cutoff, yzw = undefined */ + + STATE_TEXGEN_EYE_S, + STATE_TEXGEN_EYE_T, + STATE_TEXGEN_EYE_R, + STATE_TEXGEN_EYE_Q, + STATE_TEXGEN_OBJECT_S, + STATE_TEXGEN_OBJECT_T, + STATE_TEXGEN_OBJECT_R, + STATE_TEXGEN_OBJECT_Q, + + STATE_TEXENV_COLOR, + + STATE_DEPTH_RANGE, + + STATE_VERTEX_PROGRAM, + STATE_FRAGMENT_PROGRAM, + + STATE_ENV, + STATE_LOCAL, + + STATE_INTERNAL, /* Mesa additions */ + STATE_CURRENT_ATTRIB, /* ctx->Current vertex attrib value */ + STATE_CURRENT_ATTRIB_MAYBE_VP_CLAMPED, /* ctx->Current vertex attrib value after passthrough vertex processing */ + STATE_NORMAL_SCALE, + STATE_TEXRECT_SCALE, + STATE_FOG_PARAMS_OPTIMIZED, /* for faster fog calc */ + STATE_POINT_SIZE_CLAMPED, /* includes implementation dependent size clamp */ + STATE_POINT_SIZE_IMPL_CLAMP, /* for implementation clamp only in vs */ + STATE_LIGHT_SPOT_DIR_NORMALIZED, /* pre-normalized spot dir */ + STATE_LIGHT_POSITION, /* object vs eye space */ + STATE_LIGHT_POSITION_NORMALIZED, /* object vs eye space */ + STATE_LIGHT_HALF_VECTOR, /* object vs eye space */ + STATE_PT_SCALE, /**< Pixel transfer RGBA scale */ + STATE_PT_BIAS, /**< Pixel transfer RGBA bias */ + STATE_SHADOW_AMBIENT, /**< ARB_shadow_ambient fail value; token[2] is texture unit index */ + STATE_FB_SIZE, /**< (width-1, height-1, 0, 0) */ + STATE_FB_WPOS_Y_TRANSFORM, /**< (1, 0, -1, height-1) if a FBO is bound, (-1, height-1, 1, 0) otherwise */ + STATE_ROT_MATRIX_0, /**< ATI_envmap_bumpmap, rot matrix row 0 */ + STATE_ROT_MATRIX_1, /**< ATI_envmap_bumpmap, rot matrix row 1 */ + STATE_INTERNAL_DRIVER /* first available state index for drivers (must be last) */ +} gl_state_index; + + + +extern void +_mesa_load_state_parameters(struct gl_context *ctx, + struct gl_program_parameter_list *paramList); + + +extern GLbitfield +_mesa_program_state_flags(const gl_state_index state[STATE_LENGTH]); + + +extern char * +_mesa_program_state_string(const gl_state_index state[STATE_LENGTH]); + + +extern void +_mesa_load_tracked_matrices(struct gl_context *ctx); + + +#endif /* PROG_STATEVARS_H */ diff --git a/mesalib/src/mesa/program/programopt.c b/mesalib/src/mesa/program/programopt.c index 6601f7416..5ad9571f7 100644 --- a/mesalib/src/mesa/program/programopt.c +++ b/mesalib/src/mesa/program/programopt.c @@ -1,669 +1,670 @@ -/* - * Mesa 3-D graphics library - * Version: 6.5.3 - * - * Copyright (C) 1999-2007 Brian Paul All Rights Reserved. - * - * 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 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 - * BRIAN PAUL 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 programopt.c - * Vertex/Fragment program optimizations and transformations for program - * options, etc. - * - * \author Brian Paul - */ - - -#include "main/glheader.h" -#include "main/context.h" -#include "prog_parameter.h" -#include "prog_statevars.h" -#include "program.h" -#include "programopt.h" -#include "prog_instruction.h" - - -/** - * This function inserts instructions for coordinate modelview * projection - * into a vertex program. - * May be used to implement the position_invariant option. - */ -static void -_mesa_insert_mvp_dp4_code(struct gl_context *ctx, struct gl_vertex_program *vprog) -{ - struct prog_instruction *newInst; - const GLuint origLen = vprog->Base.NumInstructions; - const GLuint newLen = origLen + 4; - GLuint i; - - /* - * Setup state references for the modelview/projection matrix. - * XXX we should check if these state vars are already declared. - */ - static const gl_state_index mvpState[4][STATE_LENGTH] = { - { STATE_MVP_MATRIX, 0, 0, 0, 0 }, /* state.matrix.mvp.row[0] */ - { STATE_MVP_MATRIX, 0, 1, 1, 0 }, /* state.matrix.mvp.row[1] */ - { STATE_MVP_MATRIX, 0, 2, 2, 0 }, /* state.matrix.mvp.row[2] */ - { STATE_MVP_MATRIX, 0, 3, 3, 0 }, /* state.matrix.mvp.row[3] */ - }; - GLint mvpRef[4]; - - for (i = 0; i < 4; i++) { - mvpRef[i] = _mesa_add_state_reference(vprog->Base.Parameters, - mvpState[i]); - } - - /* Alloc storage for new instructions */ - newInst = _mesa_alloc_instructions(newLen); - if (!newInst) { - _mesa_error(ctx, GL_OUT_OF_MEMORY, - "glProgramString(inserting position_invariant code)"); - return; - } - - /* - * Generated instructions: - * newInst[0] = DP4 result.position.x, mvp.row[0], vertex.position; - * newInst[1] = DP4 result.position.y, mvp.row[1], vertex.position; - * newInst[2] = DP4 result.position.z, mvp.row[2], vertex.position; - * newInst[3] = DP4 result.position.w, mvp.row[3], vertex.position; - */ - _mesa_init_instructions(newInst, 4); - for (i = 0; i < 4; i++) { - newInst[i].Opcode = OPCODE_DP4; - newInst[i].DstReg.File = PROGRAM_OUTPUT; - newInst[i].DstReg.Index = VERT_RESULT_HPOS; - newInst[i].DstReg.WriteMask = (WRITEMASK_X << i); - newInst[i].SrcReg[0].File = PROGRAM_STATE_VAR; - newInst[i].SrcReg[0].Index = mvpRef[i]; - newInst[i].SrcReg[0].Swizzle = SWIZZLE_NOOP; - newInst[i].SrcReg[1].File = PROGRAM_INPUT; - newInst[i].SrcReg[1].Index = VERT_ATTRIB_POS; - newInst[i].SrcReg[1].Swizzle = SWIZZLE_NOOP; - } - - /* Append original instructions after new instructions */ - _mesa_copy_instructions (newInst + 4, vprog->Base.Instructions, origLen); - - /* free old instructions */ - _mesa_free_instructions(vprog->Base.Instructions, origLen); - - /* install new instructions */ - vprog->Base.Instructions = newInst; - vprog->Base.NumInstructions = newLen; - vprog->Base.InputsRead |= VERT_BIT_POS; - vprog->Base.OutputsWritten |= BITFIELD64_BIT(VERT_RESULT_HPOS); -} - - -static void -_mesa_insert_mvp_mad_code(struct gl_context *ctx, struct gl_vertex_program *vprog) -{ - struct prog_instruction *newInst; - const GLuint origLen = vprog->Base.NumInstructions; - const GLuint newLen = origLen + 4; - GLuint hposTemp; - GLuint i; - - /* - * Setup state references for the modelview/projection matrix. - * XXX we should check if these state vars are already declared. - */ - static const gl_state_index mvpState[4][STATE_LENGTH] = { - { STATE_MVP_MATRIX, 0, 0, 0, STATE_MATRIX_TRANSPOSE }, - { STATE_MVP_MATRIX, 0, 1, 1, STATE_MATRIX_TRANSPOSE }, - { STATE_MVP_MATRIX, 0, 2, 2, STATE_MATRIX_TRANSPOSE }, - { STATE_MVP_MATRIX, 0, 3, 3, STATE_MATRIX_TRANSPOSE }, - }; - GLint mvpRef[4]; - - for (i = 0; i < 4; i++) { - mvpRef[i] = _mesa_add_state_reference(vprog->Base.Parameters, - mvpState[i]); - } - - /* Alloc storage for new instructions */ - newInst = _mesa_alloc_instructions(newLen); - if (!newInst) { - _mesa_error(ctx, GL_OUT_OF_MEMORY, - "glProgramString(inserting position_invariant code)"); - return; - } - - /* TEMP hposTemp; */ - hposTemp = vprog->Base.NumTemporaries++; - - /* - * Generated instructions: - * emit_op2(p, OPCODE_MUL, tmp, 0, swizzle1(src,X), mat[0]); - * emit_op3(p, OPCODE_MAD, tmp, 0, swizzle1(src,Y), mat[1], tmp); - * emit_op3(p, OPCODE_MAD, tmp, 0, swizzle1(src,Z), mat[2], tmp); - * emit_op3(p, OPCODE_MAD, dest, 0, swizzle1(src,W), mat[3], tmp); - */ - _mesa_init_instructions(newInst, 4); - - newInst[0].Opcode = OPCODE_MUL; - newInst[0].DstReg.File = PROGRAM_TEMPORARY; - newInst[0].DstReg.Index = hposTemp; - newInst[0].DstReg.WriteMask = WRITEMASK_XYZW; - newInst[0].SrcReg[0].File = PROGRAM_INPUT; - newInst[0].SrcReg[0].Index = VERT_ATTRIB_POS; - newInst[0].SrcReg[0].Swizzle = SWIZZLE_XXXX; - newInst[0].SrcReg[1].File = PROGRAM_STATE_VAR; - newInst[0].SrcReg[1].Index = mvpRef[0]; - newInst[0].SrcReg[1].Swizzle = SWIZZLE_NOOP; - - for (i = 1; i <= 2; i++) { - newInst[i].Opcode = OPCODE_MAD; - newInst[i].DstReg.File = PROGRAM_TEMPORARY; - newInst[i].DstReg.Index = hposTemp; - newInst[i].DstReg.WriteMask = WRITEMASK_XYZW; - newInst[i].SrcReg[0].File = PROGRAM_INPUT; - newInst[i].SrcReg[0].Index = VERT_ATTRIB_POS; - newInst[i].SrcReg[0].Swizzle = MAKE_SWIZZLE4(i,i,i,i); - newInst[i].SrcReg[1].File = PROGRAM_STATE_VAR; - newInst[i].SrcReg[1].Index = mvpRef[i]; - newInst[i].SrcReg[1].Swizzle = SWIZZLE_NOOP; - newInst[i].SrcReg[2].File = PROGRAM_TEMPORARY; - newInst[i].SrcReg[2].Index = hposTemp; - newInst[1].SrcReg[2].Swizzle = SWIZZLE_NOOP; - } - - newInst[3].Opcode = OPCODE_MAD; - newInst[3].DstReg.File = PROGRAM_OUTPUT; - newInst[3].DstReg.Index = VERT_RESULT_HPOS; - newInst[3].DstReg.WriteMask = WRITEMASK_XYZW; - newInst[3].SrcReg[0].File = PROGRAM_INPUT; - newInst[3].SrcReg[0].Index = VERT_ATTRIB_POS; - newInst[3].SrcReg[0].Swizzle = SWIZZLE_WWWW; - newInst[3].SrcReg[1].File = PROGRAM_STATE_VAR; - newInst[3].SrcReg[1].Index = mvpRef[3]; - newInst[3].SrcReg[1].Swizzle = SWIZZLE_NOOP; - newInst[3].SrcReg[2].File = PROGRAM_TEMPORARY; - newInst[3].SrcReg[2].Index = hposTemp; - newInst[3].SrcReg[2].Swizzle = SWIZZLE_NOOP; - - - /* Append original instructions after new instructions */ - _mesa_copy_instructions (newInst + 4, vprog->Base.Instructions, origLen); - - /* free old instructions */ - _mesa_free_instructions(vprog->Base.Instructions, origLen); - - /* install new instructions */ - vprog->Base.Instructions = newInst; - vprog->Base.NumInstructions = newLen; - vprog->Base.InputsRead |= VERT_BIT_POS; - vprog->Base.OutputsWritten |= BITFIELD64_BIT(VERT_RESULT_HPOS); -} - - -void -_mesa_insert_mvp_code(struct gl_context *ctx, struct gl_vertex_program *vprog) -{ - if (ctx->mvp_with_dp4) - _mesa_insert_mvp_dp4_code( ctx, vprog ); - else - _mesa_insert_mvp_mad_code( ctx, vprog ); -} - - - - - - -/** - * Append extra instructions onto the given fragment program to implement - * the fog mode specified by fprog->FogOption. - * The fragment.fogcoord input is used to compute the fog blend factor. - * - * XXX with a little work, this function could be adapted to add fog code - * to vertex programs too. - */ -void -_mesa_append_fog_code(struct gl_context *ctx, struct gl_fragment_program *fprog) -{ - static const gl_state_index fogPStateOpt[STATE_LENGTH] - = { STATE_INTERNAL, STATE_FOG_PARAMS_OPTIMIZED, 0, 0, 0 }; - static const gl_state_index fogColorState[STATE_LENGTH] - = { STATE_FOG_COLOR, 0, 0, 0, 0}; - struct prog_instruction *newInst, *inst; - const GLuint origLen = fprog->Base.NumInstructions; - const GLuint newLen = origLen + 5; - GLuint i; - GLint fogPRefOpt, fogColorRef; /* state references */ - GLuint colorTemp, fogFactorTemp; /* temporary registerss */ - - if (fprog->FogOption == GL_NONE) { - _mesa_problem(ctx, "_mesa_append_fog_code() called for fragment program" - " with FogOption == GL_NONE"); - return; - } - - /* Alloc storage for new instructions */ - newInst = _mesa_alloc_instructions(newLen); - if (!newInst) { - _mesa_error(ctx, GL_OUT_OF_MEMORY, - "glProgramString(inserting fog_option code)"); - return; - } - - /* Copy orig instructions into new instruction buffer */ - _mesa_copy_instructions(newInst, fprog->Base.Instructions, origLen); - - /* PARAM fogParamsRefOpt = internal optimized fog params; */ - fogPRefOpt - = _mesa_add_state_reference(fprog->Base.Parameters, fogPStateOpt); - /* PARAM fogColorRef = state.fog.color; */ - fogColorRef - = _mesa_add_state_reference(fprog->Base.Parameters, fogColorState); - - /* TEMP colorTemp; */ - colorTemp = fprog->Base.NumTemporaries++; - /* TEMP fogFactorTemp; */ - fogFactorTemp = fprog->Base.NumTemporaries++; - - /* Scan program to find where result.color is written */ - inst = newInst; - for (i = 0; i < fprog->Base.NumInstructions; i++) { - if (inst->Opcode == OPCODE_END) - break; - if (inst->DstReg.File == PROGRAM_OUTPUT && - inst->DstReg.Index == FRAG_RESULT_COLOR) { - /* change the instruction to write to colorTemp w/ clamping */ - inst->DstReg.File = PROGRAM_TEMPORARY; - inst->DstReg.Index = colorTemp; - inst->SaturateMode = SATURATE_ZERO_ONE; - /* don't break (may be several writes to result.color) */ - } - inst++; - } - assert(inst->Opcode == OPCODE_END); /* we'll overwrite this inst */ - - _mesa_init_instructions(inst, 5); - - /* emit instructions to compute fog blending factor */ - if (fprog->FogOption == GL_LINEAR) { - /* MAD fogFactorTemp.x, fragment.fogcoord.x, fogPRefOpt.x, fogPRefOpt.y; */ - inst->Opcode = OPCODE_MAD; - inst->DstReg.File = PROGRAM_TEMPORARY; - inst->DstReg.Index = fogFactorTemp; - inst->DstReg.WriteMask = WRITEMASK_X; - inst->SrcReg[0].File = PROGRAM_INPUT; - inst->SrcReg[0].Index = FRAG_ATTRIB_FOGC; - inst->SrcReg[0].Swizzle = SWIZZLE_XXXX; - inst->SrcReg[1].File = PROGRAM_STATE_VAR; - inst->SrcReg[1].Index = fogPRefOpt; - inst->SrcReg[1].Swizzle = SWIZZLE_XXXX; - inst->SrcReg[2].File = PROGRAM_STATE_VAR; - inst->SrcReg[2].Index = fogPRefOpt; - inst->SrcReg[2].Swizzle = SWIZZLE_YYYY; - inst->SaturateMode = SATURATE_ZERO_ONE; - inst++; - } - else { - ASSERT(fprog->FogOption == GL_EXP || fprog->FogOption == GL_EXP2); - /* fogPRefOpt.z = d/ln(2), fogPRefOpt.w = d/sqrt(ln(2) */ - /* EXP: MUL fogFactorTemp.x, fogPRefOpt.z, fragment.fogcoord.x; */ - /* EXP2: MUL fogFactorTemp.x, fogPRefOpt.w, fragment.fogcoord.x; */ - inst->Opcode = OPCODE_MUL; - inst->DstReg.File = PROGRAM_TEMPORARY; - inst->DstReg.Index = fogFactorTemp; - inst->DstReg.WriteMask = WRITEMASK_X; - inst->SrcReg[0].File = PROGRAM_STATE_VAR; - inst->SrcReg[0].Index = fogPRefOpt; - inst->SrcReg[0].Swizzle - = (fprog->FogOption == GL_EXP) ? SWIZZLE_ZZZZ : SWIZZLE_WWWW; - inst->SrcReg[1].File = PROGRAM_INPUT; - inst->SrcReg[1].Index = FRAG_ATTRIB_FOGC; - inst->SrcReg[1].Swizzle = SWIZZLE_XXXX; - inst++; - if (fprog->FogOption == GL_EXP2) { - /* MUL fogFactorTemp.x, fogFactorTemp.x, fogFactorTemp.x; */ - inst->Opcode = OPCODE_MUL; - inst->DstReg.File = PROGRAM_TEMPORARY; - inst->DstReg.Index = fogFactorTemp; - inst->DstReg.WriteMask = WRITEMASK_X; - inst->SrcReg[0].File = PROGRAM_TEMPORARY; - inst->SrcReg[0].Index = fogFactorTemp; - inst->SrcReg[0].Swizzle = SWIZZLE_XXXX; - inst->SrcReg[1].File = PROGRAM_TEMPORARY; - inst->SrcReg[1].Index = fogFactorTemp; - inst->SrcReg[1].Swizzle = SWIZZLE_XXXX; - inst++; - } - /* EX2_SAT fogFactorTemp.x, -fogFactorTemp.x; */ - inst->Opcode = OPCODE_EX2; - inst->DstReg.File = PROGRAM_TEMPORARY; - inst->DstReg.Index = fogFactorTemp; - inst->DstReg.WriteMask = WRITEMASK_X; - inst->SrcReg[0].File = PROGRAM_TEMPORARY; - inst->SrcReg[0].Index = fogFactorTemp; - inst->SrcReg[0].Negate = NEGATE_XYZW; - inst->SrcReg[0].Swizzle = SWIZZLE_XXXX; - inst->SaturateMode = SATURATE_ZERO_ONE; - inst++; - } - /* LRP result.color.xyz, fogFactorTemp.xxxx, colorTemp, fogColorRef; */ - inst->Opcode = OPCODE_LRP; - inst->DstReg.File = PROGRAM_OUTPUT; - inst->DstReg.Index = FRAG_RESULT_COLOR; - inst->DstReg.WriteMask = WRITEMASK_XYZ; - inst->SrcReg[0].File = PROGRAM_TEMPORARY; - inst->SrcReg[0].Index = fogFactorTemp; - inst->SrcReg[0].Swizzle = SWIZZLE_XXXX; - inst->SrcReg[1].File = PROGRAM_TEMPORARY; - inst->SrcReg[1].Index = colorTemp; - inst->SrcReg[1].Swizzle = SWIZZLE_NOOP; - inst->SrcReg[2].File = PROGRAM_STATE_VAR; - inst->SrcReg[2].Index = fogColorRef; - inst->SrcReg[2].Swizzle = SWIZZLE_NOOP; - inst++; - /* MOV result.color.w, colorTemp.x; # copy alpha */ - inst->Opcode = OPCODE_MOV; - inst->DstReg.File = PROGRAM_OUTPUT; - inst->DstReg.Index = FRAG_RESULT_COLOR; - inst->DstReg.WriteMask = WRITEMASK_W; - inst->SrcReg[0].File = PROGRAM_TEMPORARY; - inst->SrcReg[0].Index = colorTemp; - inst->SrcReg[0].Swizzle = SWIZZLE_NOOP; - inst++; - /* END; */ - inst->Opcode = OPCODE_END; - inst++; - - /* free old instructions */ - _mesa_free_instructions(fprog->Base.Instructions, origLen); - - /* install new instructions */ - fprog->Base.Instructions = newInst; - fprog->Base.NumInstructions = inst - newInst; - fprog->Base.InputsRead |= FRAG_BIT_FOGC; - /* XXX do this? fprog->FogOption = GL_NONE; */ -} - - - -static GLboolean -is_texture_instruction(const struct prog_instruction *inst) -{ - switch (inst->Opcode) { - case OPCODE_TEX: - case OPCODE_TXB: - case OPCODE_TXD: - case OPCODE_TXL: - case OPCODE_TXP: - case OPCODE_TXP_NV: - return GL_TRUE; - default: - return GL_FALSE; - } -} - - -/** - * Count the number of texure indirections in the given program. - * The program's NumTexIndirections field will be updated. - * See the GL_ARB_fragment_program spec (issue 24) for details. - * XXX we count texture indirections in texenvprogram.c (maybe use this code - * instead and elsewhere). - */ -void -_mesa_count_texture_indirections(struct gl_program *prog) -{ - GLuint indirections = 1; - GLbitfield tempsOutput = 0x0; - GLbitfield aluTemps = 0x0; - GLuint i; - - for (i = 0; i < prog->NumInstructions; i++) { - const struct prog_instruction *inst = prog->Instructions + i; - - if (is_texture_instruction(inst)) { - if (((inst->SrcReg[0].File == PROGRAM_TEMPORARY) && - (tempsOutput & (1 << inst->SrcReg[0].Index))) || - ((inst->Opcode != OPCODE_KIL) && - (inst->DstReg.File == PROGRAM_TEMPORARY) && - (aluTemps & (1 << inst->DstReg.Index)))) - { - indirections++; - tempsOutput = 0x0; - aluTemps = 0x0; - } - } - else { - GLuint j; - for (j = 0; j < 3; j++) { - if (inst->SrcReg[j].File == PROGRAM_TEMPORARY) - aluTemps |= (1 << inst->SrcReg[j].Index); - } - if (inst->DstReg.File == PROGRAM_TEMPORARY) - aluTemps |= (1 << inst->DstReg.Index); - } - - if ((inst->Opcode != OPCODE_KIL) && (inst->DstReg.File == PROGRAM_TEMPORARY)) - tempsOutput |= (1 << inst->DstReg.Index); - } - - prog->NumTexIndirections = indirections; -} - - -/** - * Count number of texture instructions in given program and update the - * program's NumTexInstructions field. - */ -void -_mesa_count_texture_instructions(struct gl_program *prog) -{ - GLuint i; - prog->NumTexInstructions = 0; - for (i = 0; i < prog->NumInstructions; i++) { - prog->NumTexInstructions += is_texture_instruction(prog->Instructions + i); - } -} - - -/** - * Scan/rewrite program to remove reads of custom (output) registers. - * The passed type has to be either PROGRAM_OUTPUT or PROGRAM_VARYING - * (for vertex shaders). - * In GLSL shaders, varying vars can be read and written. - * On some hardware, trying to read an output register causes trouble. - * So, rewrite the program to use a temporary register in this case. - */ -void -_mesa_remove_output_reads(struct gl_program *prog, gl_register_file type) -{ - GLuint i; - GLint outputMap[VERT_RESULT_MAX]; - GLuint numVaryingReads = 0; - GLboolean usedTemps[MAX_PROGRAM_TEMPS]; - GLuint firstTemp = 0; - - _mesa_find_used_registers(prog, PROGRAM_TEMPORARY, - usedTemps, MAX_PROGRAM_TEMPS); - - assert(type == PROGRAM_VARYING || type == PROGRAM_OUTPUT); - assert(prog->Target == GL_VERTEX_PROGRAM_ARB || type != PROGRAM_VARYING); - - for (i = 0; i < VERT_RESULT_MAX; i++) - outputMap[i] = -1; - - /* look for instructions which read from varying vars */ - for (i = 0; i < prog->NumInstructions; i++) { - struct prog_instruction *inst = prog->Instructions + i; - const GLuint numSrc = _mesa_num_inst_src_regs(inst->Opcode); - GLuint j; - for (j = 0; j < numSrc; j++) { - if (inst->SrcReg[j].File == type) { - /* replace the read with a temp reg */ - const GLuint var = inst->SrcReg[j].Index; - if (outputMap[var] == -1) { - numVaryingReads++; - outputMap[var] = _mesa_find_free_register(usedTemps, - MAX_PROGRAM_TEMPS, - firstTemp); - firstTemp = outputMap[var] + 1; - } - inst->SrcReg[j].File = PROGRAM_TEMPORARY; - inst->SrcReg[j].Index = outputMap[var]; - } - } - } - - if (numVaryingReads == 0) - return; /* nothing to be done */ - - /* look for instructions which write to the varying vars identified above */ - for (i = 0; i < prog->NumInstructions; i++) { - struct prog_instruction *inst = prog->Instructions + i; - if (inst->DstReg.File == type && - outputMap[inst->DstReg.Index] >= 0) { - /* change inst to write to the temp reg, instead of the varying */ - inst->DstReg.File = PROGRAM_TEMPORARY; - inst->DstReg.Index = outputMap[inst->DstReg.Index]; - } - } - - /* insert new instructions to copy the temp vars to the varying vars */ - { - struct prog_instruction *inst; - GLint endPos, var; - - /* Look for END instruction and insert the new varying writes */ - endPos = -1; - for (i = 0; i < prog->NumInstructions; i++) { - struct prog_instruction *inst = prog->Instructions + i; - if (inst->Opcode == OPCODE_END) { - endPos = i; - _mesa_insert_instructions(prog, i, numVaryingReads); - break; - } - } - - assert(endPos >= 0); - - /* insert new MOV instructions here */ - inst = prog->Instructions + endPos; - for (var = 0; var < VERT_RESULT_MAX; var++) { - if (outputMap[var] >= 0) { - /* MOV VAR[var], TEMP[tmp]; */ - inst->Opcode = OPCODE_MOV; - inst->DstReg.File = type; - inst->DstReg.Index = var; - inst->SrcReg[0].File = PROGRAM_TEMPORARY; - inst->SrcReg[0].Index = outputMap[var]; - inst++; - } - } - } -} - - -/** - * Make the given fragment program into a "no-op" shader. - * Actually, just copy the incoming fragment color (or texcoord) - * to the output color. - * This is for debug/test purposes. - */ -void -_mesa_nop_fragment_program(struct gl_context *ctx, struct gl_fragment_program *prog) -{ - struct prog_instruction *inst; - GLuint inputAttr; - - inst = _mesa_alloc_instructions(2); - if (!inst) { - _mesa_error(ctx, GL_OUT_OF_MEMORY, "_mesa_nop_fragment_program"); - return; - } - - _mesa_init_instructions(inst, 2); - - inst[0].Opcode = OPCODE_MOV; - inst[0].DstReg.File = PROGRAM_OUTPUT; - inst[0].DstReg.Index = FRAG_RESULT_COLOR; - inst[0].SrcReg[0].File = PROGRAM_INPUT; - if (prog->Base.InputsRead & FRAG_BIT_COL0) - inputAttr = FRAG_ATTRIB_COL0; - else - inputAttr = FRAG_ATTRIB_TEX0; - inst[0].SrcReg[0].Index = inputAttr; - - inst[1].Opcode = OPCODE_END; - - _mesa_free_instructions(prog->Base.Instructions, - prog->Base.NumInstructions); - - prog->Base.Instructions = inst; - prog->Base.NumInstructions = 2; - prog->Base.InputsRead = 1 << inputAttr; - prog->Base.OutputsWritten = BITFIELD64_BIT(FRAG_RESULT_COLOR); -} - - -/** - * \sa _mesa_nop_fragment_program - * Replace the given vertex program with a "no-op" program that just - * transforms vertex position and emits color. - */ -void -_mesa_nop_vertex_program(struct gl_context *ctx, struct gl_vertex_program *prog) -{ - struct prog_instruction *inst; - GLuint inputAttr; - - /* - * Start with a simple vertex program that emits color. - */ - inst = _mesa_alloc_instructions(2); - if (!inst) { - _mesa_error(ctx, GL_OUT_OF_MEMORY, "_mesa_nop_vertex_program"); - return; - } - - _mesa_init_instructions(inst, 2); - - inst[0].Opcode = OPCODE_MOV; - inst[0].DstReg.File = PROGRAM_OUTPUT; - inst[0].DstReg.Index = VERT_RESULT_COL0; - inst[0].SrcReg[0].File = PROGRAM_INPUT; - if (prog->Base.InputsRead & VERT_BIT_COLOR0) - inputAttr = VERT_ATTRIB_COLOR0; - else - inputAttr = VERT_ATTRIB_TEX0; - inst[0].SrcReg[0].Index = inputAttr; - - inst[1].Opcode = OPCODE_END; - - _mesa_free_instructions(prog->Base.Instructions, - prog->Base.NumInstructions); - - prog->Base.Instructions = inst; - prog->Base.NumInstructions = 2; - prog->Base.InputsRead = 1 << inputAttr; - prog->Base.OutputsWritten = BITFIELD64_BIT(VERT_RESULT_COL0); - - /* - * Now insert code to do standard modelview/projection transformation. - */ - _mesa_insert_mvp_code(ctx, prog); -} +/* + * Mesa 3-D graphics library + * Version: 6.5.3 + * + * Copyright (C) 1999-2007 Brian Paul All Rights Reserved. + * + * 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 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 + * BRIAN PAUL 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 programopt.c + * Vertex/Fragment program optimizations and transformations for program + * options, etc. + * + * \author Brian Paul + */ + + +#include "main/glheader.h" +#include "main/context.h" +#include "prog_parameter.h" +#include "prog_statevars.h" +#include "program.h" +#include "programopt.h" +#include "prog_instruction.h" + + +/** + * This function inserts instructions for coordinate modelview * projection + * into a vertex program. + * May be used to implement the position_invariant option. + */ +static void +_mesa_insert_mvp_dp4_code(struct gl_context *ctx, struct gl_vertex_program *vprog) +{ + struct prog_instruction *newInst; + const GLuint origLen = vprog->Base.NumInstructions; + const GLuint newLen = origLen + 4; + GLuint i; + + /* + * Setup state references for the modelview/projection matrix. + * XXX we should check if these state vars are already declared. + */ + static const gl_state_index mvpState[4][STATE_LENGTH] = { + { STATE_MVP_MATRIX, 0, 0, 0, 0 }, /* state.matrix.mvp.row[0] */ + { STATE_MVP_MATRIX, 0, 1, 1, 0 }, /* state.matrix.mvp.row[1] */ + { STATE_MVP_MATRIX, 0, 2, 2, 0 }, /* state.matrix.mvp.row[2] */ + { STATE_MVP_MATRIX, 0, 3, 3, 0 }, /* state.matrix.mvp.row[3] */ + }; + GLint mvpRef[4]; + + for (i = 0; i < 4; i++) { + mvpRef[i] = _mesa_add_state_reference(vprog->Base.Parameters, + mvpState[i]); + } + + /* Alloc storage for new instructions */ + newInst = _mesa_alloc_instructions(newLen); + if (!newInst) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, + "glProgramString(inserting position_invariant code)"); + return; + } + + /* + * Generated instructions: + * newInst[0] = DP4 result.position.x, mvp.row[0], vertex.position; + * newInst[1] = DP4 result.position.y, mvp.row[1], vertex.position; + * newInst[2] = DP4 result.position.z, mvp.row[2], vertex.position; + * newInst[3] = DP4 result.position.w, mvp.row[3], vertex.position; + */ + _mesa_init_instructions(newInst, 4); + for (i = 0; i < 4; i++) { + newInst[i].Opcode = OPCODE_DP4; + newInst[i].DstReg.File = PROGRAM_OUTPUT; + newInst[i].DstReg.Index = VERT_RESULT_HPOS; + newInst[i].DstReg.WriteMask = (WRITEMASK_X << i); + newInst[i].SrcReg[0].File = PROGRAM_STATE_VAR; + newInst[i].SrcReg[0].Index = mvpRef[i]; + newInst[i].SrcReg[0].Swizzle = SWIZZLE_NOOP; + newInst[i].SrcReg[1].File = PROGRAM_INPUT; + newInst[i].SrcReg[1].Index = VERT_ATTRIB_POS; + newInst[i].SrcReg[1].Swizzle = SWIZZLE_NOOP; + } + + /* Append original instructions after new instructions */ + _mesa_copy_instructions (newInst + 4, vprog->Base.Instructions, origLen); + + /* free old instructions */ + _mesa_free_instructions(vprog->Base.Instructions, origLen); + + /* install new instructions */ + vprog->Base.Instructions = newInst; + vprog->Base.NumInstructions = newLen; + vprog->Base.InputsRead |= VERT_BIT_POS; + vprog->Base.OutputsWritten |= BITFIELD64_BIT(VERT_RESULT_HPOS); +} + + +static void +_mesa_insert_mvp_mad_code(struct gl_context *ctx, struct gl_vertex_program *vprog) +{ + struct prog_instruction *newInst; + const GLuint origLen = vprog->Base.NumInstructions; + const GLuint newLen = origLen + 4; + GLuint hposTemp; + GLuint i; + + /* + * Setup state references for the modelview/projection matrix. + * XXX we should check if these state vars are already declared. + */ + static const gl_state_index mvpState[4][STATE_LENGTH] = { + { STATE_MVP_MATRIX, 0, 0, 0, STATE_MATRIX_TRANSPOSE }, + { STATE_MVP_MATRIX, 0, 1, 1, STATE_MATRIX_TRANSPOSE }, + { STATE_MVP_MATRIX, 0, 2, 2, STATE_MATRIX_TRANSPOSE }, + { STATE_MVP_MATRIX, 0, 3, 3, STATE_MATRIX_TRANSPOSE }, + }; + GLint mvpRef[4]; + + for (i = 0; i < 4; i++) { + mvpRef[i] = _mesa_add_state_reference(vprog->Base.Parameters, + mvpState[i]); + } + + /* Alloc storage for new instructions */ + newInst = _mesa_alloc_instructions(newLen); + if (!newInst) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, + "glProgramString(inserting position_invariant code)"); + return; + } + + /* TEMP hposTemp; */ + hposTemp = vprog->Base.NumTemporaries++; + + /* + * Generated instructions: + * emit_op2(p, OPCODE_MUL, tmp, 0, swizzle1(src,X), mat[0]); + * emit_op3(p, OPCODE_MAD, tmp, 0, swizzle1(src,Y), mat[1], tmp); + * emit_op3(p, OPCODE_MAD, tmp, 0, swizzle1(src,Z), mat[2], tmp); + * emit_op3(p, OPCODE_MAD, dest, 0, swizzle1(src,W), mat[3], tmp); + */ + _mesa_init_instructions(newInst, 4); + + newInst[0].Opcode = OPCODE_MUL; + newInst[0].DstReg.File = PROGRAM_TEMPORARY; + newInst[0].DstReg.Index = hposTemp; + newInst[0].DstReg.WriteMask = WRITEMASK_XYZW; + newInst[0].SrcReg[0].File = PROGRAM_INPUT; + newInst[0].SrcReg[0].Index = VERT_ATTRIB_POS; + newInst[0].SrcReg[0].Swizzle = SWIZZLE_XXXX; + newInst[0].SrcReg[1].File = PROGRAM_STATE_VAR; + newInst[0].SrcReg[1].Index = mvpRef[0]; + newInst[0].SrcReg[1].Swizzle = SWIZZLE_NOOP; + + for (i = 1; i <= 2; i++) { + newInst[i].Opcode = OPCODE_MAD; + newInst[i].DstReg.File = PROGRAM_TEMPORARY; + newInst[i].DstReg.Index = hposTemp; + newInst[i].DstReg.WriteMask = WRITEMASK_XYZW; + newInst[i].SrcReg[0].File = PROGRAM_INPUT; + newInst[i].SrcReg[0].Index = VERT_ATTRIB_POS; + newInst[i].SrcReg[0].Swizzle = MAKE_SWIZZLE4(i,i,i,i); + newInst[i].SrcReg[1].File = PROGRAM_STATE_VAR; + newInst[i].SrcReg[1].Index = mvpRef[i]; + newInst[i].SrcReg[1].Swizzle = SWIZZLE_NOOP; + newInst[i].SrcReg[2].File = PROGRAM_TEMPORARY; + newInst[i].SrcReg[2].Index = hposTemp; + newInst[1].SrcReg[2].Swizzle = SWIZZLE_NOOP; + } + + newInst[3].Opcode = OPCODE_MAD; + newInst[3].DstReg.File = PROGRAM_OUTPUT; + newInst[3].DstReg.Index = VERT_RESULT_HPOS; + newInst[3].DstReg.WriteMask = WRITEMASK_XYZW; + newInst[3].SrcReg[0].File = PROGRAM_INPUT; + newInst[3].SrcReg[0].Index = VERT_ATTRIB_POS; + newInst[3].SrcReg[0].Swizzle = SWIZZLE_WWWW; + newInst[3].SrcReg[1].File = PROGRAM_STATE_VAR; + newInst[3].SrcReg[1].Index = mvpRef[3]; + newInst[3].SrcReg[1].Swizzle = SWIZZLE_NOOP; + newInst[3].SrcReg[2].File = PROGRAM_TEMPORARY; + newInst[3].SrcReg[2].Index = hposTemp; + newInst[3].SrcReg[2].Swizzle = SWIZZLE_NOOP; + + + /* Append original instructions after new instructions */ + _mesa_copy_instructions (newInst + 4, vprog->Base.Instructions, origLen); + + /* free old instructions */ + _mesa_free_instructions(vprog->Base.Instructions, origLen); + + /* install new instructions */ + vprog->Base.Instructions = newInst; + vprog->Base.NumInstructions = newLen; + vprog->Base.InputsRead |= VERT_BIT_POS; + vprog->Base.OutputsWritten |= BITFIELD64_BIT(VERT_RESULT_HPOS); +} + + +void +_mesa_insert_mvp_code(struct gl_context *ctx, struct gl_vertex_program *vprog) +{ + if (ctx->mvp_with_dp4) + _mesa_insert_mvp_dp4_code( ctx, vprog ); + else + _mesa_insert_mvp_mad_code( ctx, vprog ); +} + + + + + + +/** + * Append extra instructions onto the given fragment program to implement + * the fog mode specified by fprog->FogOption. + * The fragment.fogcoord input is used to compute the fog blend factor. + * + * XXX with a little work, this function could be adapted to add fog code + * to vertex programs too. + */ +void +_mesa_append_fog_code(struct gl_context *ctx, struct gl_fragment_program *fprog, GLboolean saturate) +{ + static const gl_state_index fogPStateOpt[STATE_LENGTH] + = { STATE_INTERNAL, STATE_FOG_PARAMS_OPTIMIZED, 0, 0, 0 }; + static const gl_state_index fogColorState[STATE_LENGTH] + = { STATE_FOG_COLOR, 0, 0, 0, 0}; + struct prog_instruction *newInst, *inst; + const GLuint origLen = fprog->Base.NumInstructions; + const GLuint newLen = origLen + 5; + GLuint i; + GLint fogPRefOpt, fogColorRef; /* state references */ + GLuint colorTemp, fogFactorTemp; /* temporary registerss */ + + if (fprog->FogOption == GL_NONE) { + _mesa_problem(ctx, "_mesa_append_fog_code() called for fragment program" + " with FogOption == GL_NONE"); + return; + } + + /* Alloc storage for new instructions */ + newInst = _mesa_alloc_instructions(newLen); + if (!newInst) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, + "glProgramString(inserting fog_option code)"); + return; + } + + /* Copy orig instructions into new instruction buffer */ + _mesa_copy_instructions(newInst, fprog->Base.Instructions, origLen); + + /* PARAM fogParamsRefOpt = internal optimized fog params; */ + fogPRefOpt + = _mesa_add_state_reference(fprog->Base.Parameters, fogPStateOpt); + /* PARAM fogColorRef = state.fog.color; */ + fogColorRef + = _mesa_add_state_reference(fprog->Base.Parameters, fogColorState); + + /* TEMP colorTemp; */ + colorTemp = fprog->Base.NumTemporaries++; + /* TEMP fogFactorTemp; */ + fogFactorTemp = fprog->Base.NumTemporaries++; + + /* Scan program to find where result.color is written */ + inst = newInst; + for (i = 0; i < fprog->Base.NumInstructions; i++) { + if (inst->Opcode == OPCODE_END) + break; + if (inst->DstReg.File == PROGRAM_OUTPUT && + inst->DstReg.Index == FRAG_RESULT_COLOR) { + /* change the instruction to write to colorTemp w/ clamping */ + inst->DstReg.File = PROGRAM_TEMPORARY; + inst->DstReg.Index = colorTemp; + inst->SaturateMode = saturate; + /* don't break (may be several writes to result.color) */ + } + inst++; + } + assert(inst->Opcode == OPCODE_END); /* we'll overwrite this inst */ + + _mesa_init_instructions(inst, 5); + + /* emit instructions to compute fog blending factor */ + /* this is always clamped to [0, 1] regardless of fragment clamping */ + if (fprog->FogOption == GL_LINEAR) { + /* MAD fogFactorTemp.x, fragment.fogcoord.x, fogPRefOpt.x, fogPRefOpt.y; */ + inst->Opcode = OPCODE_MAD; + inst->DstReg.File = PROGRAM_TEMPORARY; + inst->DstReg.Index = fogFactorTemp; + inst->DstReg.WriteMask = WRITEMASK_X; + inst->SrcReg[0].File = PROGRAM_INPUT; + inst->SrcReg[0].Index = FRAG_ATTRIB_FOGC; + inst->SrcReg[0].Swizzle = SWIZZLE_XXXX; + inst->SrcReg[1].File = PROGRAM_STATE_VAR; + inst->SrcReg[1].Index = fogPRefOpt; + inst->SrcReg[1].Swizzle = SWIZZLE_XXXX; + inst->SrcReg[2].File = PROGRAM_STATE_VAR; + inst->SrcReg[2].Index = fogPRefOpt; + inst->SrcReg[2].Swizzle = SWIZZLE_YYYY; + inst->SaturateMode = SATURATE_ZERO_ONE; + inst++; + } + else { + ASSERT(fprog->FogOption == GL_EXP || fprog->FogOption == GL_EXP2); + /* fogPRefOpt.z = d/ln(2), fogPRefOpt.w = d/sqrt(ln(2) */ + /* EXP: MUL fogFactorTemp.x, fogPRefOpt.z, fragment.fogcoord.x; */ + /* EXP2: MUL fogFactorTemp.x, fogPRefOpt.w, fragment.fogcoord.x; */ + inst->Opcode = OPCODE_MUL; + inst->DstReg.File = PROGRAM_TEMPORARY; + inst->DstReg.Index = fogFactorTemp; + inst->DstReg.WriteMask = WRITEMASK_X; + inst->SrcReg[0].File = PROGRAM_STATE_VAR; + inst->SrcReg[0].Index = fogPRefOpt; + inst->SrcReg[0].Swizzle + = (fprog->FogOption == GL_EXP) ? SWIZZLE_ZZZZ : SWIZZLE_WWWW; + inst->SrcReg[1].File = PROGRAM_INPUT; + inst->SrcReg[1].Index = FRAG_ATTRIB_FOGC; + inst->SrcReg[1].Swizzle = SWIZZLE_XXXX; + inst++; + if (fprog->FogOption == GL_EXP2) { + /* MUL fogFactorTemp.x, fogFactorTemp.x, fogFactorTemp.x; */ + inst->Opcode = OPCODE_MUL; + inst->DstReg.File = PROGRAM_TEMPORARY; + inst->DstReg.Index = fogFactorTemp; + inst->DstReg.WriteMask = WRITEMASK_X; + inst->SrcReg[0].File = PROGRAM_TEMPORARY; + inst->SrcReg[0].Index = fogFactorTemp; + inst->SrcReg[0].Swizzle = SWIZZLE_XXXX; + inst->SrcReg[1].File = PROGRAM_TEMPORARY; + inst->SrcReg[1].Index = fogFactorTemp; + inst->SrcReg[1].Swizzle = SWIZZLE_XXXX; + inst++; + } + /* EX2_SAT fogFactorTemp.x, -fogFactorTemp.x; */ + inst->Opcode = OPCODE_EX2; + inst->DstReg.File = PROGRAM_TEMPORARY; + inst->DstReg.Index = fogFactorTemp; + inst->DstReg.WriteMask = WRITEMASK_X; + inst->SrcReg[0].File = PROGRAM_TEMPORARY; + inst->SrcReg[0].Index = fogFactorTemp; + inst->SrcReg[0].Negate = NEGATE_XYZW; + inst->SrcReg[0].Swizzle = SWIZZLE_XXXX; + inst->SaturateMode = SATURATE_ZERO_ONE; + inst++; + } + /* LRP result.color.xyz, fogFactorTemp.xxxx, colorTemp, fogColorRef; */ + inst->Opcode = OPCODE_LRP; + inst->DstReg.File = PROGRAM_OUTPUT; + inst->DstReg.Index = FRAG_RESULT_COLOR; + inst->DstReg.WriteMask = WRITEMASK_XYZ; + inst->SrcReg[0].File = PROGRAM_TEMPORARY; + inst->SrcReg[0].Index = fogFactorTemp; + inst->SrcReg[0].Swizzle = SWIZZLE_XXXX; + inst->SrcReg[1].File = PROGRAM_TEMPORARY; + inst->SrcReg[1].Index = colorTemp; + inst->SrcReg[1].Swizzle = SWIZZLE_NOOP; + inst->SrcReg[2].File = PROGRAM_STATE_VAR; + inst->SrcReg[2].Index = fogColorRef; + inst->SrcReg[2].Swizzle = SWIZZLE_NOOP; + inst++; + /* MOV result.color.w, colorTemp.x; # copy alpha */ + inst->Opcode = OPCODE_MOV; + inst->DstReg.File = PROGRAM_OUTPUT; + inst->DstReg.Index = FRAG_RESULT_COLOR; + inst->DstReg.WriteMask = WRITEMASK_W; + inst->SrcReg[0].File = PROGRAM_TEMPORARY; + inst->SrcReg[0].Index = colorTemp; + inst->SrcReg[0].Swizzle = SWIZZLE_NOOP; + inst++; + /* END; */ + inst->Opcode = OPCODE_END; + inst++; + + /* free old instructions */ + _mesa_free_instructions(fprog->Base.Instructions, origLen); + + /* install new instructions */ + fprog->Base.Instructions = newInst; + fprog->Base.NumInstructions = inst - newInst; + fprog->Base.InputsRead |= FRAG_BIT_FOGC; + /* XXX do this? fprog->FogOption = GL_NONE; */ +} + + + +static GLboolean +is_texture_instruction(const struct prog_instruction *inst) +{ + switch (inst->Opcode) { + case OPCODE_TEX: + case OPCODE_TXB: + case OPCODE_TXD: + case OPCODE_TXL: + case OPCODE_TXP: + case OPCODE_TXP_NV: + return GL_TRUE; + default: + return GL_FALSE; + } +} + + +/** + * Count the number of texure indirections in the given program. + * The program's NumTexIndirections field will be updated. + * See the GL_ARB_fragment_program spec (issue 24) for details. + * XXX we count texture indirections in texenvprogram.c (maybe use this code + * instead and elsewhere). + */ +void +_mesa_count_texture_indirections(struct gl_program *prog) +{ + GLuint indirections = 1; + GLbitfield tempsOutput = 0x0; + GLbitfield aluTemps = 0x0; + GLuint i; + + for (i = 0; i < prog->NumInstructions; i++) { + const struct prog_instruction *inst = prog->Instructions + i; + + if (is_texture_instruction(inst)) { + if (((inst->SrcReg[0].File == PROGRAM_TEMPORARY) && + (tempsOutput & (1 << inst->SrcReg[0].Index))) || + ((inst->Opcode != OPCODE_KIL) && + (inst->DstReg.File == PROGRAM_TEMPORARY) && + (aluTemps & (1 << inst->DstReg.Index)))) + { + indirections++; + tempsOutput = 0x0; + aluTemps = 0x0; + } + } + else { + GLuint j; + for (j = 0; j < 3; j++) { + if (inst->SrcReg[j].File == PROGRAM_TEMPORARY) + aluTemps |= (1 << inst->SrcReg[j].Index); + } + if (inst->DstReg.File == PROGRAM_TEMPORARY) + aluTemps |= (1 << inst->DstReg.Index); + } + + if ((inst->Opcode != OPCODE_KIL) && (inst->DstReg.File == PROGRAM_TEMPORARY)) + tempsOutput |= (1 << inst->DstReg.Index); + } + + prog->NumTexIndirections = indirections; +} + + +/** + * Count number of texture instructions in given program and update the + * program's NumTexInstructions field. + */ +void +_mesa_count_texture_instructions(struct gl_program *prog) +{ + GLuint i; + prog->NumTexInstructions = 0; + for (i = 0; i < prog->NumInstructions; i++) { + prog->NumTexInstructions += is_texture_instruction(prog->Instructions + i); + } +} + + +/** + * Scan/rewrite program to remove reads of custom (output) registers. + * The passed type has to be either PROGRAM_OUTPUT or PROGRAM_VARYING + * (for vertex shaders). + * In GLSL shaders, varying vars can be read and written. + * On some hardware, trying to read an output register causes trouble. + * So, rewrite the program to use a temporary register in this case. + */ +void +_mesa_remove_output_reads(struct gl_program *prog, gl_register_file type) +{ + GLuint i; + GLint outputMap[VERT_RESULT_MAX]; + GLuint numVaryingReads = 0; + GLboolean usedTemps[MAX_PROGRAM_TEMPS]; + GLuint firstTemp = 0; + + _mesa_find_used_registers(prog, PROGRAM_TEMPORARY, + usedTemps, MAX_PROGRAM_TEMPS); + + assert(type == PROGRAM_VARYING || type == PROGRAM_OUTPUT); + assert(prog->Target == GL_VERTEX_PROGRAM_ARB || type != PROGRAM_VARYING); + + for (i = 0; i < VERT_RESULT_MAX; i++) + outputMap[i] = -1; + + /* look for instructions which read from varying vars */ + for (i = 0; i < prog->NumInstructions; i++) { + struct prog_instruction *inst = prog->Instructions + i; + const GLuint numSrc = _mesa_num_inst_src_regs(inst->Opcode); + GLuint j; + for (j = 0; j < numSrc; j++) { + if (inst->SrcReg[j].File == type) { + /* replace the read with a temp reg */ + const GLuint var = inst->SrcReg[j].Index; + if (outputMap[var] == -1) { + numVaryingReads++; + outputMap[var] = _mesa_find_free_register(usedTemps, + MAX_PROGRAM_TEMPS, + firstTemp); + firstTemp = outputMap[var] + 1; + } + inst->SrcReg[j].File = PROGRAM_TEMPORARY; + inst->SrcReg[j].Index = outputMap[var]; + } + } + } + + if (numVaryingReads == 0) + return; /* nothing to be done */ + + /* look for instructions which write to the varying vars identified above */ + for (i = 0; i < prog->NumInstructions; i++) { + struct prog_instruction *inst = prog->Instructions + i; + if (inst->DstReg.File == type && + outputMap[inst->DstReg.Index] >= 0) { + /* change inst to write to the temp reg, instead of the varying */ + inst->DstReg.File = PROGRAM_TEMPORARY; + inst->DstReg.Index = outputMap[inst->DstReg.Index]; + } + } + + /* insert new instructions to copy the temp vars to the varying vars */ + { + struct prog_instruction *inst; + GLint endPos, var; + + /* Look for END instruction and insert the new varying writes */ + endPos = -1; + for (i = 0; i < prog->NumInstructions; i++) { + struct prog_instruction *inst = prog->Instructions + i; + if (inst->Opcode == OPCODE_END) { + endPos = i; + _mesa_insert_instructions(prog, i, numVaryingReads); + break; + } + } + + assert(endPos >= 0); + + /* insert new MOV instructions here */ + inst = prog->Instructions + endPos; + for (var = 0; var < VERT_RESULT_MAX; var++) { + if (outputMap[var] >= 0) { + /* MOV VAR[var], TEMP[tmp]; */ + inst->Opcode = OPCODE_MOV; + inst->DstReg.File = type; + inst->DstReg.Index = var; + inst->SrcReg[0].File = PROGRAM_TEMPORARY; + inst->SrcReg[0].Index = outputMap[var]; + inst++; + } + } + } +} + + +/** + * Make the given fragment program into a "no-op" shader. + * Actually, just copy the incoming fragment color (or texcoord) + * to the output color. + * This is for debug/test purposes. + */ +void +_mesa_nop_fragment_program(struct gl_context *ctx, struct gl_fragment_program *prog) +{ + struct prog_instruction *inst; + GLuint inputAttr; + + inst = _mesa_alloc_instructions(2); + if (!inst) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "_mesa_nop_fragment_program"); + return; + } + + _mesa_init_instructions(inst, 2); + + inst[0].Opcode = OPCODE_MOV; + inst[0].DstReg.File = PROGRAM_OUTPUT; + inst[0].DstReg.Index = FRAG_RESULT_COLOR; + inst[0].SrcReg[0].File = PROGRAM_INPUT; + if (prog->Base.InputsRead & FRAG_BIT_COL0) + inputAttr = FRAG_ATTRIB_COL0; + else + inputAttr = FRAG_ATTRIB_TEX0; + inst[0].SrcReg[0].Index = inputAttr; + + inst[1].Opcode = OPCODE_END; + + _mesa_free_instructions(prog->Base.Instructions, + prog->Base.NumInstructions); + + prog->Base.Instructions = inst; + prog->Base.NumInstructions = 2; + prog->Base.InputsRead = 1 << inputAttr; + prog->Base.OutputsWritten = BITFIELD64_BIT(FRAG_RESULT_COLOR); +} + + +/** + * \sa _mesa_nop_fragment_program + * Replace the given vertex program with a "no-op" program that just + * transforms vertex position and emits color. + */ +void +_mesa_nop_vertex_program(struct gl_context *ctx, struct gl_vertex_program *prog) +{ + struct prog_instruction *inst; + GLuint inputAttr; + + /* + * Start with a simple vertex program that emits color. + */ + inst = _mesa_alloc_instructions(2); + if (!inst) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "_mesa_nop_vertex_program"); + return; + } + + _mesa_init_instructions(inst, 2); + + inst[0].Opcode = OPCODE_MOV; + inst[0].DstReg.File = PROGRAM_OUTPUT; + inst[0].DstReg.Index = VERT_RESULT_COL0; + inst[0].SrcReg[0].File = PROGRAM_INPUT; + if (prog->Base.InputsRead & VERT_BIT_COLOR0) + inputAttr = VERT_ATTRIB_COLOR0; + else + inputAttr = VERT_ATTRIB_TEX0; + inst[0].SrcReg[0].Index = inputAttr; + + inst[1].Opcode = OPCODE_END; + + _mesa_free_instructions(prog->Base.Instructions, + prog->Base.NumInstructions); + + prog->Base.Instructions = inst; + prog->Base.NumInstructions = 2; + prog->Base.InputsRead = 1 << inputAttr; + prog->Base.OutputsWritten = BITFIELD64_BIT(VERT_RESULT_COL0); + + /* + * Now insert code to do standard modelview/projection transformation. + */ + _mesa_insert_mvp_code(ctx, prog); +} diff --git a/mesalib/src/mesa/program/programopt.h b/mesalib/src/mesa/program/programopt.h index 2e07d8989..79631aa58 100644 --- a/mesalib/src/mesa/program/programopt.h +++ b/mesalib/src/mesa/program/programopt.h @@ -1,53 +1,53 @@ -/* - * Mesa 3-D graphics library - * Version: 6.5.3 - * - * Copyright (C) 1999-2007 Brian Paul All Rights Reserved. - * - * 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 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 - * BRIAN PAUL 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. - */ - - -#ifndef PROGRAMOPT_H -#define PROGRAMOPT_H 1 - -#include "main/mtypes.h" - -extern void -_mesa_insert_mvp_code(struct gl_context *ctx, struct gl_vertex_program *vprog); - -extern void -_mesa_append_fog_code(struct gl_context *ctx, struct gl_fragment_program *fprog); - -extern void -_mesa_count_texture_indirections(struct gl_program *prog); - -extern void -_mesa_count_texture_instructions(struct gl_program *prog); - -extern void -_mesa_remove_output_reads(struct gl_program *prog, gl_register_file type); - -extern void -_mesa_nop_fragment_program(struct gl_context *ctx, struct gl_fragment_program *prog); - -extern void -_mesa_nop_vertex_program(struct gl_context *ctx, struct gl_vertex_program *prog); - - -#endif /* PROGRAMOPT_H */ +/* + * Mesa 3-D graphics library + * Version: 6.5.3 + * + * Copyright (C) 1999-2007 Brian Paul All Rights Reserved. + * + * 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 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 + * BRIAN PAUL 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. + */ + + +#ifndef PROGRAMOPT_H +#define PROGRAMOPT_H 1 + +#include "main/mtypes.h" + +extern void +_mesa_insert_mvp_code(struct gl_context *ctx, struct gl_vertex_program *vprog); + +extern void +_mesa_append_fog_code(struct gl_context *ctx, struct gl_fragment_program *fprog, GLboolean saturate); + +extern void +_mesa_count_texture_indirections(struct gl_program *prog); + +extern void +_mesa_count_texture_instructions(struct gl_program *prog); + +extern void +_mesa_remove_output_reads(struct gl_program *prog, gl_register_file type); + +extern void +_mesa_nop_fragment_program(struct gl_context *ctx, struct gl_fragment_program *prog); + +extern void +_mesa_nop_vertex_program(struct gl_context *ctx, struct gl_vertex_program *prog); + + +#endif /* PROGRAMOPT_H */ -- cgit v1.2.3