diff options
Diffstat (limited to 'mesalib/src/mesa/program')
19 files changed, 14067 insertions, 14049 deletions
diff --git a/mesalib/src/mesa/program/arbprogparse.c b/mesalib/src/mesa/program/arbprogparse.c index dffc8abf7..bca033477 100644 --- a/mesalib/src/mesa/program/arbprogparse.c +++ b/mesalib/src/mesa/program/arbprogparse.c @@ -1,216 +1,216 @@ -/* - * 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; - program->OriginUpperLeft = state.option.OriginUpperLeft; - program->PixelCenterInteger = state.option.PixelCenterInteger; - - program->UsesKill = state.fragment.UsesKill; - - 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 (state.option.Fog != OPTION_NONE) { - static const GLenum fog_modes[4] = { - GL_NONE, GL_EXP, GL_EXP2, GL_LINEAR - }; - - /* 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, fog_modes[state.option.Fog], GL_TRUE); - } - -#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;
+ program->OriginUpperLeft = state.option.OriginUpperLeft;
+ program->PixelCenterInteger = state.option.PixelCenterInteger;
+
+ program->UsesKill = state.fragment.UsesKill;
+
+ 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 (state.option.Fog != OPTION_NONE) {
+ static const GLenum fog_modes[4] = {
+ GL_NONE, GL_EXP, GL_EXP2, GL_LINEAR
+ };
+
+ /* 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, fog_modes[state.option.Fog], GL_TRUE);
+ }
+
+#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/doflexbison.bat b/mesalib/src/mesa/program/doflexbison.bat new file mode 100644 index 000000000..d54e5ac65 --- /dev/null +++ b/mesalib/src/mesa/program/doflexbison.bat @@ -0,0 +1,18 @@ +@echo off
+setlocal
+
+cd "%~dp0"
+
+set M4=..\..\..\..\tools\mhmake\m4.exe
+set BISON_PKGDATADIR=../../../../tools/mhmake/src/bisondata
+
+set path=..\..\..\..\tools\mhmake;%path%
+
+..\..\..\..\tools\mhmake\bison.exe -v -d --output=program_parse.tab.c program_parse.y
+
+copy "..\..\..\..\tools\mhmake\flex++.exe" flex.exe
+flex.exe --never-interactive --outfile=lex.yy.c program_lexer.l
+del flex.exe
+
+endlocal
+
diff --git a/mesalib/src/mesa/program/hash_table.c b/mesalib/src/mesa/program/hash_table.c index 877a9e2ff..3e9b9d4d3 100644 --- a/mesalib/src/mesa/program/hash_table.c +++ b/mesalib/src/mesa/program/hash_table.c @@ -1,209 +1,209 @@ -/* - * Copyright © 2008 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 hash_table.c - * \brief Implementation of a generic, opaque hash table data type. - * - * \author Ian Romanick <ian.d.romanick@intel.com> - */ - -#include "main/imports.h" -#include "main/simple_list.h" -#include "hash_table.h" - -struct node { - struct node *next; - struct node *prev; -}; - -struct hash_table { - hash_func_t hash; - hash_compare_func_t compare; - - unsigned num_buckets; - struct node buckets[1]; -}; - - -struct hash_node { - struct node link; - const void *key; - void *data; -}; - - -struct hash_table * -hash_table_ctor(unsigned num_buckets, hash_func_t hash, - hash_compare_func_t compare) -{ - struct hash_table *ht; - unsigned i; - - - if (num_buckets < 16) { - num_buckets = 16; - } - - ht = malloc(sizeof(*ht) + ((num_buckets - 1) - * sizeof(ht->buckets[0]))); - if (ht != NULL) { - ht->hash = hash; - ht->compare = compare; - ht->num_buckets = num_buckets; - - for (i = 0; i < num_buckets; i++) { - make_empty_list(& ht->buckets[i]); - } - } - - return ht; -} - - -void -hash_table_dtor(struct hash_table *ht) -{ - hash_table_clear(ht); - free(ht); -} - - -void -hash_table_clear(struct hash_table *ht) -{ - struct node *node; - struct node *temp; - unsigned i; - - - for (i = 0; i < ht->num_buckets; i++) { - foreach_s(node, temp, & ht->buckets[i]) { - remove_from_list(node); - free(node); - } - - assert(is_empty_list(& ht->buckets[i])); - } -} - - -void * -hash_table_find(struct hash_table *ht, const void *key) -{ - const unsigned hash_value = (*ht->hash)(key); - const unsigned bucket = hash_value % ht->num_buckets; - struct node *node; - - foreach(node, & ht->buckets[bucket]) { - struct hash_node *hn = (struct hash_node *) node; - - if ((*ht->compare)(hn->key, key) == 0) { - return hn->data; - } - } - - return NULL; -} - - -void -hash_table_insert(struct hash_table *ht, void *data, const void *key) -{ - const unsigned hash_value = (*ht->hash)(key); - const unsigned bucket = hash_value % ht->num_buckets; - struct hash_node *node; - - node = calloc(1, sizeof(*node)); - - node->data = data; - node->key = key; - - insert_at_head(& ht->buckets[bucket], & node->link); -} - -void -hash_table_remove(struct hash_table *ht, const void *key) -{ - const unsigned hash_value = (*ht->hash)(key); - const unsigned bucket = hash_value % ht->num_buckets; - struct node *node; - - foreach(node, & ht->buckets[bucket]) { - struct hash_node *hn = (struct hash_node *) node; - - if ((*ht->compare)(hn->key, key) == 0) { - remove_from_list(node); - free(node); - return; - } - } -} - -void -hash_table_call_foreach(struct hash_table *ht, - void (*callback)(const void *key, - void *data, - void *closure), - void *closure) -{ - int bucket; - - for (bucket = 0; bucket < ht->num_buckets; bucket++) { - struct node *node, *temp; - foreach_s(node, temp, &ht->buckets[bucket]) { - struct hash_node *hn = (struct hash_node *) node; - - callback(hn->key, hn->data, closure); - } - } -} - -unsigned -hash_table_string_hash(const void *key) -{ - const char *str = (const char *) key; - unsigned hash = 5381; - - - while (*str != '\0') { - hash = (hash * 33) + *str; - str++; - } - - return hash; -} - - -unsigned -hash_table_pointer_hash(const void *key) -{ - return (unsigned)((uintptr_t) key / sizeof(void *)); -} - - -int -hash_table_pointer_compare(const void *key1, const void *key2) -{ - return key1 == key2 ? 0 : 1; -} +/*
+ * Copyright © 2008 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 hash_table.c
+ * \brief Implementation of a generic, opaque hash table data type.
+ *
+ * \author Ian Romanick <ian.d.romanick@intel.com>
+ */
+
+#include "main/imports.h"
+#include "main/simple_list.h"
+#include "hash_table.h"
+
+struct node {
+ struct node *next;
+ struct node *prev;
+};
+
+struct hash_table {
+ hash_func_t hash;
+ hash_compare_func_t compare;
+
+ unsigned num_buckets;
+ struct node buckets[1];
+};
+
+
+struct hash_node {
+ struct node link;
+ const void *key;
+ void *data;
+};
+
+
+struct hash_table *
+hash_table_ctor(unsigned num_buckets, hash_func_t hash,
+ hash_compare_func_t compare)
+{
+ struct hash_table *ht;
+ unsigned i;
+
+
+ if (num_buckets < 16) {
+ num_buckets = 16;
+ }
+
+ ht = malloc(sizeof(*ht) + ((num_buckets - 1)
+ * sizeof(ht->buckets[0])));
+ if (ht != NULL) {
+ ht->hash = hash;
+ ht->compare = compare;
+ ht->num_buckets = num_buckets;
+
+ for (i = 0; i < num_buckets; i++) {
+ make_empty_list(& ht->buckets[i]);
+ }
+ }
+
+ return ht;
+}
+
+
+void
+hash_table_dtor(struct hash_table *ht)
+{
+ hash_table_clear(ht);
+ free(ht);
+}
+
+
+void
+hash_table_clear(struct hash_table *ht)
+{
+ struct node *node;
+ struct node *temp;
+ unsigned i;
+
+
+ for (i = 0; i < ht->num_buckets; i++) {
+ foreach_s(node, temp, & ht->buckets[i]) {
+ remove_from_list(node);
+ free(node);
+ }
+
+ assert(is_empty_list(& ht->buckets[i]));
+ }
+}
+
+
+void *
+hash_table_find(struct hash_table *ht, const void *key)
+{
+ const unsigned hash_value = (*ht->hash)(key);
+ const unsigned bucket = hash_value % ht->num_buckets;
+ struct node *node;
+
+ foreach(node, & ht->buckets[bucket]) {
+ struct hash_node *hn = (struct hash_node *) node;
+
+ if ((*ht->compare)(hn->key, key) == 0) {
+ return hn->data;
+ }
+ }
+
+ return NULL;
+}
+
+
+void
+hash_table_insert(struct hash_table *ht, void *data, const void *key)
+{
+ const unsigned hash_value = (*ht->hash)(key);
+ const unsigned bucket = hash_value % ht->num_buckets;
+ struct hash_node *node;
+
+ node = calloc(1, sizeof(*node));
+
+ node->data = data;
+ node->key = key;
+
+ insert_at_head(& ht->buckets[bucket], & node->link);
+}
+
+void
+hash_table_remove(struct hash_table *ht, const void *key)
+{
+ const unsigned hash_value = (*ht->hash)(key);
+ const unsigned bucket = hash_value % ht->num_buckets;
+ struct node *node;
+
+ foreach(node, & ht->buckets[bucket]) {
+ struct hash_node *hn = (struct hash_node *) node;
+
+ if ((*ht->compare)(hn->key, key) == 0) {
+ remove_from_list(node);
+ free(node);
+ return;
+ }
+ }
+}
+
+void
+hash_table_call_foreach(struct hash_table *ht,
+ void (*callback)(const void *key,
+ void *data,
+ void *closure),
+ void *closure)
+{
+ int bucket;
+
+ for (bucket = 0; bucket < ht->num_buckets; bucket++) {
+ struct node *node, *temp;
+ foreach_s(node, temp, &ht->buckets[bucket]) {
+ struct hash_node *hn = (struct hash_node *) node;
+
+ callback(hn->key, hn->data, closure);
+ }
+ }
+}
+
+unsigned
+hash_table_string_hash(const void *key)
+{
+ const char *str = (const char *) key;
+ unsigned hash = 5381;
+
+
+ while (*str != '\0') {
+ hash = (hash * 33) + *str;
+ str++;
+ }
+
+ return hash;
+}
+
+
+unsigned
+hash_table_pointer_hash(const void *key)
+{
+ return (unsigned)((uintptr_t) key / sizeof(void *));
+}
+
+
+int
+hash_table_pointer_compare(const void *key1, const void *key2)
+{
+ return key1 == key2 ? 0 : 1;
+}
diff --git a/mesalib/src/mesa/program/hash_table.h b/mesalib/src/mesa/program/hash_table.h index e715bb1cc..3910d0624 100644 --- a/mesalib/src/mesa/program/hash_table.h +++ b/mesalib/src/mesa/program/hash_table.h @@ -1,157 +1,157 @@ -/* - * Copyright © 2008 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 hash_table.h - * \brief Implementation of a generic, opaque hash table data type. - * - * \author Ian Romanick <ian.d.romanick@intel.com> - */ - -#ifndef HASH_TABLE_H -#define HASH_TABLE_H - -struct hash_table; - -typedef unsigned (*hash_func_t)(const void *key); -typedef int (*hash_compare_func_t)(const void *key1, const void *key2); - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Hash table constructor - * - * Creates a hash table with the specified number of buckets. The supplied - * \c hash and \c compare routines are used when adding elements to the table - * and when searching for elements in the table. - * - * \param num_buckets Number of buckets (bins) in the hash table. - * \param hash Function used to compute hash value of input keys. - * \param compare Function used to compare keys. - */ -extern struct hash_table *hash_table_ctor(unsigned num_buckets, - hash_func_t hash, hash_compare_func_t compare); - - -/** - * Release all memory associated with a hash table - * - * \warning - * This function cannot release memory occupied either by keys or data. - */ -extern void hash_table_dtor(struct hash_table *ht); - - -/** - * Flush all entries from a hash table - * - * \param ht Table to be cleared of its entries. - */ -extern void hash_table_clear(struct hash_table *ht); - - -/** - * Search a hash table for a specific element - * - * \param ht Table to be searched - * \param key Key of the desired element - * - * \return - * The \c data value supplied to \c hash_table_insert when the element with - * the matching key was added. If no matching key exists in the table, - * \c NULL is returned. - */ -extern void *hash_table_find(struct hash_table *ht, const void *key); - - -/** - * Add an element to a hash table - */ -extern void hash_table_insert(struct hash_table *ht, void *data, - const void *key); - -/** - * Remove a specific element from a hash table. - */ -extern void hash_table_remove(struct hash_table *ht, const void *key); - -/** - * Compute hash value of a string - * - * Computes the hash value of a string using the DJB2 algorithm developed by - * Professor Daniel J. Bernstein. It was published on comp.lang.c once upon - * a time. I was unable to find the original posting in the archives. - * - * \param key Pointer to a NUL terminated string to be hashed. - * - * \sa hash_table_string_compare - */ -extern unsigned hash_table_string_hash(const void *key); - - -/** - * Compare two strings used as keys - * - * This is just a macro wrapper around \c strcmp. - * - * \sa hash_table_string_hash - */ -#define hash_table_string_compare ((hash_compare_func_t) strcmp) - - -/** - * Compute hash value of a pointer - * - * \param key Pointer to be used as a hash key - * - * \note - * The memory pointed to by \c key is \b never accessed. The value of \c key - * itself is used as the hash key - * - * \sa hash_table_pointer_compare - */ -unsigned -hash_table_pointer_hash(const void *key); - - -/** - * Compare two pointers used as keys - * - * \sa hash_table_pointer_hash - */ -int -hash_table_pointer_compare(const void *key1, const void *key2); - -void -hash_table_call_foreach(struct hash_table *ht, - void (*callback)(const void *key, - void *data, - void *closure), - void *closure); - -#ifdef __cplusplus -} -#endif -#endif /* HASH_TABLE_H */ +/*
+ * Copyright © 2008 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 hash_table.h
+ * \brief Implementation of a generic, opaque hash table data type.
+ *
+ * \author Ian Romanick <ian.d.romanick@intel.com>
+ */
+
+#ifndef HASH_TABLE_H
+#define HASH_TABLE_H
+
+struct hash_table;
+
+typedef unsigned (*hash_func_t)(const void *key);
+typedef int (*hash_compare_func_t)(const void *key1, const void *key2);
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Hash table constructor
+ *
+ * Creates a hash table with the specified number of buckets. The supplied
+ * \c hash and \c compare routines are used when adding elements to the table
+ * and when searching for elements in the table.
+ *
+ * \param num_buckets Number of buckets (bins) in the hash table.
+ * \param hash Function used to compute hash value of input keys.
+ * \param compare Function used to compare keys.
+ */
+extern struct hash_table *hash_table_ctor(unsigned num_buckets,
+ hash_func_t hash, hash_compare_func_t compare);
+
+
+/**
+ * Release all memory associated with a hash table
+ *
+ * \warning
+ * This function cannot release memory occupied either by keys or data.
+ */
+extern void hash_table_dtor(struct hash_table *ht);
+
+
+/**
+ * Flush all entries from a hash table
+ *
+ * \param ht Table to be cleared of its entries.
+ */
+extern void hash_table_clear(struct hash_table *ht);
+
+
+/**
+ * Search a hash table for a specific element
+ *
+ * \param ht Table to be searched
+ * \param key Key of the desired element
+ *
+ * \return
+ * The \c data value supplied to \c hash_table_insert when the element with
+ * the matching key was added. If no matching key exists in the table,
+ * \c NULL is returned.
+ */
+extern void *hash_table_find(struct hash_table *ht, const void *key);
+
+
+/**
+ * Add an element to a hash table
+ */
+extern void hash_table_insert(struct hash_table *ht, void *data,
+ const void *key);
+
+/**
+ * Remove a specific element from a hash table.
+ */
+extern void hash_table_remove(struct hash_table *ht, const void *key);
+
+/**
+ * Compute hash value of a string
+ *
+ * Computes the hash value of a string using the DJB2 algorithm developed by
+ * Professor Daniel J. Bernstein. It was published on comp.lang.c once upon
+ * a time. I was unable to find the original posting in the archives.
+ *
+ * \param key Pointer to a NUL terminated string to be hashed.
+ *
+ * \sa hash_table_string_compare
+ */
+extern unsigned hash_table_string_hash(const void *key);
+
+
+/**
+ * Compare two strings used as keys
+ *
+ * This is just a macro wrapper around \c strcmp.
+ *
+ * \sa hash_table_string_hash
+ */
+#define hash_table_string_compare ((hash_compare_func_t) strcmp)
+
+
+/**
+ * Compute hash value of a pointer
+ *
+ * \param key Pointer to be used as a hash key
+ *
+ * \note
+ * The memory pointed to by \c key is \b never accessed. The value of \c key
+ * itself is used as the hash key
+ *
+ * \sa hash_table_pointer_compare
+ */
+unsigned
+hash_table_pointer_hash(const void *key);
+
+
+/**
+ * Compare two pointers used as keys
+ *
+ * \sa hash_table_pointer_hash
+ */
+int
+hash_table_pointer_compare(const void *key1, const void *key2);
+
+void
+hash_table_call_foreach(struct hash_table *ht,
+ void (*callback)(const void *key,
+ void *data,
+ void *closure),
+ void *closure);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* HASH_TABLE_H */
diff --git a/mesalib/src/mesa/program/ir_to_mesa.cpp b/mesalib/src/mesa/program/ir_to_mesa.cpp index 510aeab82..ebeaa9ce3 100644 --- a/mesalib/src/mesa/program/ir_to_mesa.cpp +++ b/mesalib/src/mesa/program/ir_to_mesa.cpp @@ -1,3284 +1,3284 @@ -/* - * 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 <stdio.h> -#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" -} - -class src_reg; -class dst_reg; - -static int swizzle_for_size(int size); - -/** - * This struct is a corresponding struct to Mesa prog_src_register, with - * wider fields. - */ -class src_reg { -public: - src_reg(gl_register_file file, int index, const glsl_type *type) - { - this->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; - } - - src_reg() - { - this->file = PROGRAM_UNDEFINED; - this->index = 0; - this->swizzle = 0; - this->negate = 0; - this->reladdr = NULL; - } - - explicit src_reg(dst_reg reg); - - 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. */ - src_reg *reladdr; -}; - -class dst_reg { -public: - dst_reg(gl_register_file file, int writemask) - { - this->file = file; - this->index = 0; - this->writemask = writemask; - this->cond_mask = COND_TR; - this->reladdr = NULL; - } - - dst_reg() - { - this->file = PROGRAM_UNDEFINED; - this->index = 0; - this->writemask = 0; - this->cond_mask = COND_TR; - this->reladdr = NULL; - } - - explicit dst_reg(src_reg reg); - - gl_register_file 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. */ - src_reg *reladdr; -}; - -src_reg::src_reg(dst_reg reg) -{ - this->file = reg.file; - this->index = reg.index; - this->swizzle = SWIZZLE_XYZW; - this->negate = 0; - this->reladdr = NULL; -} - -dst_reg::dst_reg(src_reg reg) -{ - this->file = reg.file; - this->index = reg.index; - this->writemask = WRITEMASK_XYZW; - this->cond_mask = COND_TR; - this->reladdr = reg.reladdr; -} - -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; - dst_reg dst; - src_reg src[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. */ - 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); - - src_reg get_temp(const glsl_type *type); - void reladdr_to_temp(ir_instruction *ir, src_reg *reg, int *num_reladdr); - - 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 *); - /*@}*/ - - 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 *emit(ir_instruction *ir, enum prog_opcode op); - - ir_to_mesa_instruction *emit(ir_instruction *ir, enum prog_opcode op, - dst_reg dst, src_reg src0); - - ir_to_mesa_instruction *emit(ir_instruction *ir, enum prog_opcode op, - dst_reg dst, src_reg src0, src_reg src1); - - ir_to_mesa_instruction *emit(ir_instruction *ir, enum prog_opcode op, - dst_reg dst, - src_reg src0, src_reg src1, src_reg src2); - - /** - * Emit the correct dot-product instruction for the type of arguments - */ - void emit_dp(ir_instruction *ir, - dst_reg dst, - src_reg src0, - src_reg src1, - unsigned elements); - - void emit_scalar(ir_instruction *ir, enum prog_opcode op, - dst_reg dst, src_reg src0); - - void emit_scalar(ir_instruction *ir, enum prog_opcode op, - dst_reg dst, src_reg src0, src_reg src1); - - void emit_scs(ir_instruction *ir, enum prog_opcode op, - dst_reg dst, const 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; -}; - -src_reg undef_src = src_reg(PROGRAM_UNDEFINED, 0, NULL); - -dst_reg undef_dst = dst_reg(PROGRAM_UNDEFINED, SWIZZLE_NOOP); - -dst_reg address_reg = dst_reg(PROGRAM_ADDRESS, WRITEMASK_X); - -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::emit(ir_instruction *ir, enum prog_opcode op, - dst_reg dst, - src_reg src0, src_reg src1, 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) { - emit(ir, OPCODE_ARL, address_reg, *dst.reladdr); - num_reladdr--; - } - assert(num_reladdr == 0); - - inst->op = op; - inst->dst = dst; - inst->src[0] = src0; - inst->src[1] = src1; - inst->src[2] = src2; - inst->ir = ir; - - inst->function = NULL; - - this->instructions.push_tail(inst); - - return inst; -} - - -ir_to_mesa_instruction * -ir_to_mesa_visitor::emit(ir_instruction *ir, enum prog_opcode op, - dst_reg dst, src_reg src0, src_reg src1) -{ - return emit(ir, op, dst, src0, src1, undef_src); -} - -ir_to_mesa_instruction * -ir_to_mesa_visitor::emit(ir_instruction *ir, enum prog_opcode op, - dst_reg dst, src_reg src0) -{ - assert(dst.writemask != 0); - return emit(ir, op, dst, src0, undef_src, undef_src); -} - -ir_to_mesa_instruction * -ir_to_mesa_visitor::emit(ir_instruction *ir, enum prog_opcode op) -{ - return emit(ir, op, undef_dst, undef_src, undef_src, undef_src); -} - -void -ir_to_mesa_visitor::emit_dp(ir_instruction *ir, - dst_reg dst, src_reg src0, src_reg src1, - unsigned elements) -{ - static const gl_inst_opcode dot_opcodes[] = { - OPCODE_DP2, OPCODE_DP3, OPCODE_DP4 - }; - - emit(ir, dot_opcodes[elements - 2], dst, src0, src1); -} - -/** - * 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::emit_scalar(ir_instruction *ir, enum prog_opcode op, - dst_reg dst, - src_reg orig_src0, 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; - src_reg src0 = orig_src0; - 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 = emit(ir, op, dst, src0, src1); - inst->dst.writemask = this_mask; - done_mask |= this_mask; - } -} - -void -ir_to_mesa_visitor::emit_scalar(ir_instruction *ir, enum prog_opcode op, - dst_reg dst, src_reg src0) -{ - src_reg undef = undef_src; - - undef.swizzle = SWIZZLE_XXXX; - - emit_scalar(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, - dst_reg dst, - const src_reg &src) -{ - /* Vertex programs cannot use the SCS opcode. - */ - if (this->prog->Target == GL_VERTEX_PROGRAM_ARB) { - emit_scalar(ir, op, dst, src); - return; - } - - const unsigned component = (op == OPCODE_SIN) ? 0 : 1; - const unsigned scs_mask = (1U << component); - int done_mask = ~dst.writemask; - 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); - 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; - dst_reg tmp_dst = dst_reg(tmp); - - /* Emit the SCS instruction. - */ - inst = emit(ir, OPCODE_SCS, tmp_dst, src0); - inst->dst.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 = emit(ir, OPCODE_SCS, dst, tmp); - inst->dst.writemask = this_mask; - } else { - /* Emit the SCS instruction to write directly to the destination. - */ - ir_to_mesa_instruction *inst = emit(ir, OPCODE_SCS, dst, src0); - inst->dst.writemask = scs_mask; - } - - done_mask |= this_mask; - } -} - -struct src_reg -ir_to_mesa_visitor::src_reg_for_float(float val) -{ - src_reg src(PROGRAM_CONSTANT, -1, NULL); - - src.index = _mesa_add_unnamed_constant(this->prog->Parameters, - &val, 1, &src.swizzle); - - return src; -} - -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. - */ -src_reg -ir_to_mesa_visitor::get_temp(const glsl_type *type) -{ - src_reg src; - int swizzle[4]; - int i; - - src.file = PROGRAM_TEMPORARY; - src.index = next_temp; - src.reladdr = NULL; - next_temp += type_size(type); - - if (type->is_array() || type->is_record()) { - src.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.swizzle = MAKE_SWIZZLE4(swizzle[0], swizzle[1], - swizzle[2], swizzle[3]); - } - src.negate = 0; - - return src; -} - -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; - 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 = 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((int) 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 = dst_reg(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 { - src_reg src(PROGRAM_STATE_VAR, index, NULL); - src.swizzle = slots[i].swizzle; - emit(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 + (int) 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; - } - - emit(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; - } - - emit(NULL, OPCODE_ENDLOOP); -} - -void -ir_to_mesa_visitor::visit(ir_loop_jump *ir) -{ - switch (ir->mode) { - case ir_loop_jump::jump_break: - emit(NULL, OPCODE_BRK); - break; - case ir_loop_jump::jump_continue: - emit(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; - 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); - emit(ir, OPCODE_MAD, dst_reg(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); - src_reg src = this->result; - - this->result = get_temp(ir->type); - ir_to_mesa_instruction *inst; - inst = emit(ir, OPCODE_MOV, dst_reg(this->result), src); - inst->saturate = true; - - return true; -} - -void -ir_to_mesa_visitor::reladdr_to_temp(ir_instruction *ir, - src_reg *reg, int *num_reladdr) -{ - if (!reg->reladdr) - return; - - emit(ir, OPCODE_ARL, address_reg, *reg->reladdr); - - if (*num_reladdr != 1) { - src_reg temp = get_temp(glsl_type::vec4_type); - - emit(ir, OPCODE_MOV, dst_reg(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); - } - - 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 src_reg result_src = get_temp(ir->type); - dst_reg result_dst = dst_reg(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; - - emit(ir, OPCODE_SWZ, result_dst, src); - this->result = result_src; -} - -void -ir_to_mesa_visitor::visit(ir_expression *ir) -{ - unsigned int operand; - src_reg op[Elements(ir->operands)]; - src_reg result_src; - 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 = dst_reg(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: - emit(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: - emit(ir, OPCODE_ABS, result_dst, op[0]); - break; - case ir_unop_sign: - emit(ir, OPCODE_SSG, result_dst, op[0]); - break; - case ir_unop_rcp: - emit_scalar(ir, OPCODE_RCP, result_dst, op[0]); - break; - - case ir_unop_exp2: - emit_scalar(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: - emit_scalar(ir, OPCODE_LG2, result_dst, op[0]); - break; - case ir_unop_sin: - emit_scalar(ir, OPCODE_SIN, result_dst, op[0]); - break; - case ir_unop_cos: - emit_scalar(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: - emit(ir, OPCODE_DDX, result_dst, op[0]); - break; - case ir_unop_dFdy: - emit(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)); - - emit(ir, opcode, result_dst, op[0]); - break; - } - - case ir_binop_add: - emit(ir, OPCODE_ADD, result_dst, op[0], op[1]); - break; - case ir_binop_sub: - emit(ir, OPCODE_SUB, result_dst, op[0], op[1]); - break; - - case ir_binop_mul: - emit(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: - emit(ir, OPCODE_SLT, result_dst, op[0], op[1]); - break; - case ir_binop_greater: - emit(ir, OPCODE_SGT, result_dst, op[0], op[1]); - break; - case ir_binop_lequal: - emit(ir, OPCODE_SLE, result_dst, op[0], op[1]); - break; - case ir_binop_gequal: - emit(ir, OPCODE_SGE, result_dst, op[0], op[1]); - break; - case ir_binop_equal: - emit(ir, OPCODE_SEQ, result_dst, op[0], op[1]); - break; - case ir_binop_nequal: - emit(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()) { - src_reg temp = get_temp(glsl_type::vec4_type); - emit(ir, OPCODE_SNE, dst_reg(temp), op[0], op[1]); - emit_dp(ir, result_dst, temp, temp, vector_elements); - emit(ir, OPCODE_SEQ, result_dst, result_src, src_reg_for_float(0.0)); - } else { - emit(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()) { - src_reg temp = get_temp(glsl_type::vec4_type); - emit(ir, OPCODE_SNE, dst_reg(temp), op[0], op[1]); - emit_dp(ir, result_dst, temp, temp, vector_elements); - emit(ir, OPCODE_SNE, result_dst, result_src, src_reg_for_float(0.0)); - } else { - emit(ir, OPCODE_SNE, result_dst, op[0], op[1]); - } - break; - - case ir_unop_any: - assert(ir->operands[0]->type->is_vector()); - emit_dp(ir, result_dst, op[0], op[0], - ir->operands[0]->type->vector_elements); - emit(ir, OPCODE_SNE, result_dst, result_src, src_reg_for_float(0.0)); - break; - - case ir_binop_logic_xor: - emit(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. */ - emit(ir, OPCODE_ADD, result_dst, op[0], op[1]); - emit(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". */ - emit(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); - 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). */ - emit_scalar(ir, OPCODE_RSQ, result_dst, op[0]); - emit(ir, OPCODE_MUL, result_dst, result_src, op[0]); - /* For incoming channels <= 0, set the result to 0. */ - op[0].negate = ~op[0].negate; - emit(ir, OPCODE_CMP, result_dst, - op[0], result_src, src_reg_for_float(0.0)); - break; - case ir_unop_rsq: - emit_scalar(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: - emit(ir, OPCODE_TRUNC, result_dst, op[0]); - break; - case ir_unop_f2b: - case ir_unop_i2b: - emit(ir, OPCODE_SNE, result_dst, - op[0], src_reg_for_float(0.0)); - break; - case ir_unop_trunc: - emit(ir, OPCODE_TRUNC, result_dst, op[0]); - break; - case ir_unop_ceil: - op[0].negate = ~op[0].negate; - emit(ir, OPCODE_FLR, result_dst, op[0]); - result_src.negate = ~result_src.negate; - break; - case ir_unop_floor: - emit(ir, OPCODE_FLR, result_dst, op[0]); - break; - case ir_unop_fract: - emit(ir, OPCODE_FRC, result_dst, op[0]); - break; - - case ir_binop_min: - emit(ir, OPCODE_MIN, result_dst, op[0], op[1]); - break; - case ir_binop_max: - emit(ir, OPCODE_MAX, result_dst, op[0], op[1]); - break; - case ir_binop_pow: - emit_scalar(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) -{ - src_reg src; - 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 = this->result; - assert(src.file != PROGRAM_UNDEFINED); - - for (i = 0; i < 4; i++) { - if (i < ir->type->vector_elements) { - switch (i) { - case 0: - swizzle[i] = GET_SWZ(src.swizzle, ir->mask.x); - break; - case 1: - swizzle[i] = GET_SWZ(src.swizzle, ir->mask.y); - break; - case 2: - swizzle[i] = GET_SWZ(src.swizzle, ir->mask.z); - break; - case 3: - swizzle[i] = GET_SWZ(src.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.swizzle = MAKE_SWIZZLE4(swizzle[0], swizzle[1], swizzle[2], swizzle[3]); - - this->result = src; -} - -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 = src_reg(entry->file, entry->index, var->type); -} - -void -ir_to_mesa_visitor::visit(ir_dereference_array *ir) -{ - ir_constant *index; - src_reg src; - int element_size = type_size(ir->type); - - index = ir->array_index->constant_expression_value(); - - ir->array->accept(this); - src = this->result; - - if (index) { - src.index += index->value.i[0] * element_size; - } else { - /* 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); - - src_reg index_reg; - - if (element_size == 1) { - index_reg = this->result; - } else { - index_reg = get_temp(glsl_type::float_type); - - emit(ir, OPCODE_MUL, dst_reg(index_reg), - this->result, src_reg_for_float(element_size)); - } - - src.reladdr = ralloc(mem_ctx, src_reg); - memcpy(src.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.swizzle = swizzle_for_size(ir->type->vector_elements); - else - src.swizzle = SWIZZLE_NOOP; - - this->result = src; -} - -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 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 dst_reg(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) -{ - dst_reg l; - 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); - src_reg condition = this->result; - - for (i = 0; i < type_size(ir->lhs->type); i++) { - if (switch_order) { - emit(ir, OPCODE_CMP, l, condition, src_reg(l), r); - } else { - emit(ir, OPCODE_CMP, l, condition, r, src_reg(l)); - } - - l.index++; - r.index++; - } - } else { - for (i = 0; i < type_size(ir->lhs->type); i++) { - emit(ir, OPCODE_MOV, l, r); - l.index++; - r.index++; - } - } -} - - -void -ir_to_mesa_visitor::visit(ir_constant *ir) -{ - src_reg src; - 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) { - src_reg temp_base = get_temp(ir->type); - dst_reg temp = dst_reg(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 = this->result; - - for (i = 0; i < (unsigned int)size; i++) { - emit(ir, OPCODE_MOV, temp, src); - - src.index++; - temp.index++; - } - } - this->result = temp_base; - return; - } - - if (ir->type->is_array()) { - src_reg temp_base = get_temp(ir->type); - dst_reg temp = dst_reg(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 = this->result; - for (int j = 0; j < size; j++) { - emit(ir, OPCODE_MOV, temp, src); - - src.index++; - temp.index++; - } - } - this->result = temp_base; - return; - } - - if (ir->type->is_matrix()) { - src_reg mat = get_temp(ir->type); - dst_reg mat_column = dst_reg(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 = src_reg(PROGRAM_CONSTANT, -1, NULL); - src.index = _mesa_add_unnamed_constant(this->prog->Parameters, - values, - ir->type->vector_elements, - &src.swizzle); - emit(ir, OPCODE_MOV, mat_column, src); - - mat_column.index++; - } - - this->result = mat; - return; - } - - src.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 = 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 = undef_src; - } - - 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); - src_reg r = this->result; - - 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++) { - emit(ir, OPCODE_MOV, l, r); - l.index++; - r.index++; - } - } - - sig_iter.next(); - } - assert(!sig_iter.has_next()); - - /* Emit call instruction */ - call_inst = emit(ir, OPCODE_CAL); - 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); - - 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); - dst_reg l = dst_reg(this->result); - - for (i = 0; i < type_size(param->type); i++) { - emit(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) -{ - src_reg result_src, coord, lod_info, projector, dx, dy; - 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 = dst_reg(coord); - emit(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 = dst_reg(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: - opcode = OPCODE_TXD; - ir->lod_info.grad.dPdx->accept(this); - dx = this->result; - ir->lod_info.grad.dPdy->accept(this); - dy = this->result; - break; - 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; - emit(ir, OPCODE_MOV, coord_dst, projector); - coord_dst.writemask = WRITEMASK_XYZW; - opcode = OPCODE_TXP; - } else { - 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; - emit(ir, OPCODE_RCP, coord_dst, projector); - - /* In the case where we have to project the coordinates "by hand," - * the shadow comparitor value must also be projected. - */ - src_reg tmp_src = coord; - if (ir->shadow_comparitor) { - /* Slot the shadow value in as the second to last component of the - * coord. - */ - ir->shadow_comparitor->accept(this); - - tmp_src = get_temp(glsl_type::vec4_type); - dst_reg tmp_dst = dst_reg(tmp_src); - - tmp_dst.writemask = WRITEMASK_Z; - emit(ir, OPCODE_MOV, tmp_dst, this->result); - - tmp_dst.writemask = WRITEMASK_XY; - emit(ir, OPCODE_MOV, tmp_dst, coord); - } - - coord_dst.writemask = WRITEMASK_XYZ; - emit(ir, OPCODE_MUL, coord_dst, tmp_src, coord_w); - - coord_dst.writemask = WRITEMASK_XYZW; - coord.swizzle = SWIZZLE_XYZW; - } - } - - /* If projection is done and the opcode is not OPCODE_TXP, then the shadow - * comparitor was put in the correct place (and projected) by the code, - * above, that handles by-hand projection. - */ - if (ir->shadow_comparitor && (!ir->projector || opcode == OPCODE_TXP)) { - /* Slot the shadow value in as the second to last component of the - * coord. - */ - ir->shadow_comparitor->accept(this); - coord_dst.writemask = WRITEMASK_Z; - emit(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; - emit(ir, OPCODE_MOV, coord_dst, lod_info); - coord_dst.writemask = WRITEMASK_XYZW; - } - - if (opcode == OPCODE_TXD) - inst = emit(ir, opcode, result_dst, coord, dx, dy); - else - inst = emit(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()) { - dst_reg l; - int i; - - assert(current_function); - - ir->get_value()->accept(this); - src_reg r = this->result; - - l = dst_reg(current_function->return_reg); - - for (i = 0; i < type_size(current_function->sig->return_type); i++) { - emit(ir, OPCODE_MOV, l, r); - l.index++; - r.index++; - } - } - - emit(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; - emit(ir, OPCODE_KIL, undef_dst, this->result); - } else { - emit(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; - 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) { - src_reg temp = get_temp(glsl_type::bool_type); - cond_inst = emit(ir->condition, OPCODE_MOV, dst_reg(temp), result); - } - cond_inst->cond_update = GL_TRUE; - - if_inst = emit(ir->condition, OPCODE_IF); - if_inst->dst.cond_mask = COND_NE; - } else { - if_inst = emit(ir->condition, OPCODE_IF, undef_dst, this->result); - } - - this->instructions.push_tail(if_inst); - - visit_exec_list(&ir->then_instructions, this); - - if (!ir->else_instructions.is_empty()) { - emit(ir->condition, OPCODE_ELSE); - visit_exec_list(&ir->else_instructions, this); - } - - if_inst = emit(ir->condition, OPCODE_ENDIF); -} - -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(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.file != PROGRAM_TEMPORARY - || inst->dst.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[r].index * 4; - - if (inst->src[r].file != PROGRAM_TEMPORARY || - inst->src[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[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[0].file != copy_chan->src[0].file || - first->src[0].index != copy_chan->src[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[r].file = first->src[0].file; - inst->src[r].index = first->src[0].index; - - int swizzle = 0; - for (int i = 0; i < 4; i++) { - int src_chan = GET_SWZ(inst->src[r].swizzle, i); - ir_to_mesa_instruction *copy_inst = acp[acp_base + src_chan]; - swizzle |= (GET_SWZ(copy_inst->src[0].swizzle, src_chan) << - (3 * i)); - } - inst->src[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.file == PROGRAM_TEMPORARY && inst->dst.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.file == PROGRAM_OUTPUT && - inst->dst.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[0].file == PROGRAM_OUTPUT) - acp[4 * r + c] = NULL; - } - } - } else if (inst->dst.file == PROGRAM_TEMPORARY || - inst->dst.file == PROGRAM_OUTPUT) { - /* Clear where it's used as dst. */ - if (inst->dst.file == PROGRAM_TEMPORARY) { - for (int c = 0; c < 4; c++) { - if (inst->dst.writemask & (1 << c)) { - acp[4 * inst->dst.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[0].swizzle, c); - - if (acp[4 * r + c]->src[0].file == inst->dst.file && - acp[4 * r + c]->src[0].index == inst->dst.index && - inst->dst.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.file == PROGRAM_TEMPORARY && - !inst->dst.reladdr && - !inst->saturate && - !inst->src[0].reladdr && - !inst->src[0].negate) { - for (int i = 0; i < 4; i++) { - if (inst->dst.writemask & (1 << i)) { - acp[4 * inst->dst.index + i] = inst; - acp_level[4 * inst->dst.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.emit(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.emit(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.emit(NULL, OPCODE_RET); - - ir_to_mesa_instruction *end; - end = v.emit(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.file; - mesa_inst->DstReg.Index = inst->dst.index; - mesa_inst->DstReg.CondMask = inst->dst.cond_mask; - mesa_inst->DstReg.WriteMask = inst->dst.writemask; - mesa_inst->DstReg.RelAddr = inst->dst.reladdr != NULL; - mesa_inst->SrcReg[0] = mesa_src_reg_from_ir_src_reg(inst->src[0]); - mesa_inst->SrcReg[1] = mesa_src_reg_from_ir_src_reg(inst->src[1]); - mesa_inst->SrcReg[2] = mesa_src_reg_from_ir_src_reg(inst->src[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 <stdio.h>
+#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"
+}
+
+class src_reg;
+class dst_reg;
+
+static int swizzle_for_size(int size);
+
+/**
+ * This struct is a corresponding struct to Mesa prog_src_register, with
+ * wider fields.
+ */
+class src_reg {
+public:
+ src_reg(gl_register_file file, int index, const glsl_type *type)
+ {
+ this->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;
+ }
+
+ src_reg()
+ {
+ this->file = PROGRAM_UNDEFINED;
+ this->index = 0;
+ this->swizzle = 0;
+ this->negate = 0;
+ this->reladdr = NULL;
+ }
+
+ explicit src_reg(dst_reg reg);
+
+ 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. */
+ src_reg *reladdr;
+};
+
+class dst_reg {
+public:
+ dst_reg(gl_register_file file, int writemask)
+ {
+ this->file = file;
+ this->index = 0;
+ this->writemask = writemask;
+ this->cond_mask = COND_TR;
+ this->reladdr = NULL;
+ }
+
+ dst_reg()
+ {
+ this->file = PROGRAM_UNDEFINED;
+ this->index = 0;
+ this->writemask = 0;
+ this->cond_mask = COND_TR;
+ this->reladdr = NULL;
+ }
+
+ explicit dst_reg(src_reg reg);
+
+ gl_register_file 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. */
+ src_reg *reladdr;
+};
+
+src_reg::src_reg(dst_reg reg)
+{
+ this->file = reg.file;
+ this->index = reg.index;
+ this->swizzle = SWIZZLE_XYZW;
+ this->negate = 0;
+ this->reladdr = NULL;
+}
+
+dst_reg::dst_reg(src_reg reg)
+{
+ this->file = reg.file;
+ this->index = reg.index;
+ this->writemask = WRITEMASK_XYZW;
+ this->cond_mask = COND_TR;
+ this->reladdr = reg.reladdr;
+}
+
+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;
+ dst_reg dst;
+ src_reg src[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. */
+ 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);
+
+ src_reg get_temp(const glsl_type *type);
+ void reladdr_to_temp(ir_instruction *ir, src_reg *reg, int *num_reladdr);
+
+ 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 *);
+ /*@}*/
+
+ 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 *emit(ir_instruction *ir, enum prog_opcode op);
+
+ ir_to_mesa_instruction *emit(ir_instruction *ir, enum prog_opcode op,
+ dst_reg dst, src_reg src0);
+
+ ir_to_mesa_instruction *emit(ir_instruction *ir, enum prog_opcode op,
+ dst_reg dst, src_reg src0, src_reg src1);
+
+ ir_to_mesa_instruction *emit(ir_instruction *ir, enum prog_opcode op,
+ dst_reg dst,
+ src_reg src0, src_reg src1, src_reg src2);
+
+ /**
+ * Emit the correct dot-product instruction for the type of arguments
+ */
+ void emit_dp(ir_instruction *ir,
+ dst_reg dst,
+ src_reg src0,
+ src_reg src1,
+ unsigned elements);
+
+ void emit_scalar(ir_instruction *ir, enum prog_opcode op,
+ dst_reg dst, src_reg src0);
+
+ void emit_scalar(ir_instruction *ir, enum prog_opcode op,
+ dst_reg dst, src_reg src0, src_reg src1);
+
+ void emit_scs(ir_instruction *ir, enum prog_opcode op,
+ dst_reg dst, const 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;
+};
+
+src_reg undef_src = src_reg(PROGRAM_UNDEFINED, 0, NULL);
+
+dst_reg undef_dst = dst_reg(PROGRAM_UNDEFINED, SWIZZLE_NOOP);
+
+dst_reg address_reg = dst_reg(PROGRAM_ADDRESS, WRITEMASK_X);
+
+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::emit(ir_instruction *ir, enum prog_opcode op,
+ dst_reg dst,
+ src_reg src0, src_reg src1, 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) {
+ emit(ir, OPCODE_ARL, address_reg, *dst.reladdr);
+ num_reladdr--;
+ }
+ assert(num_reladdr == 0);
+
+ inst->op = op;
+ inst->dst = dst;
+ inst->src[0] = src0;
+ inst->src[1] = src1;
+ inst->src[2] = src2;
+ inst->ir = ir;
+
+ inst->function = NULL;
+
+ this->instructions.push_tail(inst);
+
+ return inst;
+}
+
+
+ir_to_mesa_instruction *
+ir_to_mesa_visitor::emit(ir_instruction *ir, enum prog_opcode op,
+ dst_reg dst, src_reg src0, src_reg src1)
+{
+ return emit(ir, op, dst, src0, src1, undef_src);
+}
+
+ir_to_mesa_instruction *
+ir_to_mesa_visitor::emit(ir_instruction *ir, enum prog_opcode op,
+ dst_reg dst, src_reg src0)
+{
+ assert(dst.writemask != 0);
+ return emit(ir, op, dst, src0, undef_src, undef_src);
+}
+
+ir_to_mesa_instruction *
+ir_to_mesa_visitor::emit(ir_instruction *ir, enum prog_opcode op)
+{
+ return emit(ir, op, undef_dst, undef_src, undef_src, undef_src);
+}
+
+void
+ir_to_mesa_visitor::emit_dp(ir_instruction *ir,
+ dst_reg dst, src_reg src0, src_reg src1,
+ unsigned elements)
+{
+ static const gl_inst_opcode dot_opcodes[] = {
+ OPCODE_DP2, OPCODE_DP3, OPCODE_DP4
+ };
+
+ emit(ir, dot_opcodes[elements - 2], dst, src0, src1);
+}
+
+/**
+ * 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::emit_scalar(ir_instruction *ir, enum prog_opcode op,
+ dst_reg dst,
+ src_reg orig_src0, 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;
+ src_reg src0 = orig_src0;
+ 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 = emit(ir, op, dst, src0, src1);
+ inst->dst.writemask = this_mask;
+ done_mask |= this_mask;
+ }
+}
+
+void
+ir_to_mesa_visitor::emit_scalar(ir_instruction *ir, enum prog_opcode op,
+ dst_reg dst, src_reg src0)
+{
+ src_reg undef = undef_src;
+
+ undef.swizzle = SWIZZLE_XXXX;
+
+ emit_scalar(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,
+ dst_reg dst,
+ const src_reg &src)
+{
+ /* Vertex programs cannot use the SCS opcode.
+ */
+ if (this->prog->Target == GL_VERTEX_PROGRAM_ARB) {
+ emit_scalar(ir, op, dst, src);
+ return;
+ }
+
+ const unsigned component = (op == OPCODE_SIN) ? 0 : 1;
+ const unsigned scs_mask = (1U << component);
+ int done_mask = ~dst.writemask;
+ 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);
+ 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;
+ dst_reg tmp_dst = dst_reg(tmp);
+
+ /* Emit the SCS instruction.
+ */
+ inst = emit(ir, OPCODE_SCS, tmp_dst, src0);
+ inst->dst.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 = emit(ir, OPCODE_SCS, dst, tmp);
+ inst->dst.writemask = this_mask;
+ } else {
+ /* Emit the SCS instruction to write directly to the destination.
+ */
+ ir_to_mesa_instruction *inst = emit(ir, OPCODE_SCS, dst, src0);
+ inst->dst.writemask = scs_mask;
+ }
+
+ done_mask |= this_mask;
+ }
+}
+
+struct src_reg
+ir_to_mesa_visitor::src_reg_for_float(float val)
+{
+ src_reg src(PROGRAM_CONSTANT, -1, NULL);
+
+ src.index = _mesa_add_unnamed_constant(this->prog->Parameters,
+ &val, 1, &src.swizzle);
+
+ return src;
+}
+
+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.
+ */
+src_reg
+ir_to_mesa_visitor::get_temp(const glsl_type *type)
+{
+ src_reg src;
+ int swizzle[4];
+ int i;
+
+ src.file = PROGRAM_TEMPORARY;
+ src.index = next_temp;
+ src.reladdr = NULL;
+ next_temp += type_size(type);
+
+ if (type->is_array() || type->is_record()) {
+ src.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.swizzle = MAKE_SWIZZLE4(swizzle[0], swizzle[1],
+ swizzle[2], swizzle[3]);
+ }
+ src.negate = 0;
+
+ return src;
+}
+
+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;
+ 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 = 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((int) 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 = dst_reg(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 {
+ src_reg src(PROGRAM_STATE_VAR, index, NULL);
+ src.swizzle = slots[i].swizzle;
+ emit(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 + (int) 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;
+ }
+
+ emit(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;
+ }
+
+ emit(NULL, OPCODE_ENDLOOP);
+}
+
+void
+ir_to_mesa_visitor::visit(ir_loop_jump *ir)
+{
+ switch (ir->mode) {
+ case ir_loop_jump::jump_break:
+ emit(NULL, OPCODE_BRK);
+ break;
+ case ir_loop_jump::jump_continue:
+ emit(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;
+ 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);
+ emit(ir, OPCODE_MAD, dst_reg(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);
+ src_reg src = this->result;
+
+ this->result = get_temp(ir->type);
+ ir_to_mesa_instruction *inst;
+ inst = emit(ir, OPCODE_MOV, dst_reg(this->result), src);
+ inst->saturate = true;
+
+ return true;
+}
+
+void
+ir_to_mesa_visitor::reladdr_to_temp(ir_instruction *ir,
+ src_reg *reg, int *num_reladdr)
+{
+ if (!reg->reladdr)
+ return;
+
+ emit(ir, OPCODE_ARL, address_reg, *reg->reladdr);
+
+ if (*num_reladdr != 1) {
+ src_reg temp = get_temp(glsl_type::vec4_type);
+
+ emit(ir, OPCODE_MOV, dst_reg(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);
+ }
+
+ 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 src_reg result_src = get_temp(ir->type);
+ dst_reg result_dst = dst_reg(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;
+
+ emit(ir, OPCODE_SWZ, result_dst, src);
+ this->result = result_src;
+}
+
+void
+ir_to_mesa_visitor::visit(ir_expression *ir)
+{
+ unsigned int operand;
+ src_reg op[Elements(ir->operands)];
+ src_reg result_src;
+ 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 = dst_reg(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:
+ emit(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:
+ emit(ir, OPCODE_ABS, result_dst, op[0]);
+ break;
+ case ir_unop_sign:
+ emit(ir, OPCODE_SSG, result_dst, op[0]);
+ break;
+ case ir_unop_rcp:
+ emit_scalar(ir, OPCODE_RCP, result_dst, op[0]);
+ break;
+
+ case ir_unop_exp2:
+ emit_scalar(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:
+ emit_scalar(ir, OPCODE_LG2, result_dst, op[0]);
+ break;
+ case ir_unop_sin:
+ emit_scalar(ir, OPCODE_SIN, result_dst, op[0]);
+ break;
+ case ir_unop_cos:
+ emit_scalar(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:
+ emit(ir, OPCODE_DDX, result_dst, op[0]);
+ break;
+ case ir_unop_dFdy:
+ emit(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));
+
+ emit(ir, opcode, result_dst, op[0]);
+ break;
+ }
+
+ case ir_binop_add:
+ emit(ir, OPCODE_ADD, result_dst, op[0], op[1]);
+ break;
+ case ir_binop_sub:
+ emit(ir, OPCODE_SUB, result_dst, op[0], op[1]);
+ break;
+
+ case ir_binop_mul:
+ emit(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:
+ emit(ir, OPCODE_SLT, result_dst, op[0], op[1]);
+ break;
+ case ir_binop_greater:
+ emit(ir, OPCODE_SGT, result_dst, op[0], op[1]);
+ break;
+ case ir_binop_lequal:
+ emit(ir, OPCODE_SLE, result_dst, op[0], op[1]);
+ break;
+ case ir_binop_gequal:
+ emit(ir, OPCODE_SGE, result_dst, op[0], op[1]);
+ break;
+ case ir_binop_equal:
+ emit(ir, OPCODE_SEQ, result_dst, op[0], op[1]);
+ break;
+ case ir_binop_nequal:
+ emit(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()) {
+ src_reg temp = get_temp(glsl_type::vec4_type);
+ emit(ir, OPCODE_SNE, dst_reg(temp), op[0], op[1]);
+ emit_dp(ir, result_dst, temp, temp, vector_elements);
+ emit(ir, OPCODE_SEQ, result_dst, result_src, src_reg_for_float(0.0));
+ } else {
+ emit(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()) {
+ src_reg temp = get_temp(glsl_type::vec4_type);
+ emit(ir, OPCODE_SNE, dst_reg(temp), op[0], op[1]);
+ emit_dp(ir, result_dst, temp, temp, vector_elements);
+ emit(ir, OPCODE_SNE, result_dst, result_src, src_reg_for_float(0.0));
+ } else {
+ emit(ir, OPCODE_SNE, result_dst, op[0], op[1]);
+ }
+ break;
+
+ case ir_unop_any:
+ assert(ir->operands[0]->type->is_vector());
+ emit_dp(ir, result_dst, op[0], op[0],
+ ir->operands[0]->type->vector_elements);
+ emit(ir, OPCODE_SNE, result_dst, result_src, src_reg_for_float(0.0));
+ break;
+
+ case ir_binop_logic_xor:
+ emit(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. */
+ emit(ir, OPCODE_ADD, result_dst, op[0], op[1]);
+ emit(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". */
+ emit(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);
+ 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). */
+ emit_scalar(ir, OPCODE_RSQ, result_dst, op[0]);
+ emit(ir, OPCODE_MUL, result_dst, result_src, op[0]);
+ /* For incoming channels <= 0, set the result to 0. */
+ op[0].negate = ~op[0].negate;
+ emit(ir, OPCODE_CMP, result_dst,
+ op[0], result_src, src_reg_for_float(0.0));
+ break;
+ case ir_unop_rsq:
+ emit_scalar(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:
+ emit(ir, OPCODE_TRUNC, result_dst, op[0]);
+ break;
+ case ir_unop_f2b:
+ case ir_unop_i2b:
+ emit(ir, OPCODE_SNE, result_dst,
+ op[0], src_reg_for_float(0.0));
+ break;
+ case ir_unop_trunc:
+ emit(ir, OPCODE_TRUNC, result_dst, op[0]);
+ break;
+ case ir_unop_ceil:
+ op[0].negate = ~op[0].negate;
+ emit(ir, OPCODE_FLR, result_dst, op[0]);
+ result_src.negate = ~result_src.negate;
+ break;
+ case ir_unop_floor:
+ emit(ir, OPCODE_FLR, result_dst, op[0]);
+ break;
+ case ir_unop_fract:
+ emit(ir, OPCODE_FRC, result_dst, op[0]);
+ break;
+
+ case ir_binop_min:
+ emit(ir, OPCODE_MIN, result_dst, op[0], op[1]);
+ break;
+ case ir_binop_max:
+ emit(ir, OPCODE_MAX, result_dst, op[0], op[1]);
+ break;
+ case ir_binop_pow:
+ emit_scalar(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)
+{
+ src_reg src;
+ 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 = this->result;
+ assert(src.file != PROGRAM_UNDEFINED);
+
+ for (i = 0; i < 4; i++) {
+ if (i < ir->type->vector_elements) {
+ switch (i) {
+ case 0:
+ swizzle[i] = GET_SWZ(src.swizzle, ir->mask.x);
+ break;
+ case 1:
+ swizzle[i] = GET_SWZ(src.swizzle, ir->mask.y);
+ break;
+ case 2:
+ swizzle[i] = GET_SWZ(src.swizzle, ir->mask.z);
+ break;
+ case 3:
+ swizzle[i] = GET_SWZ(src.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.swizzle = MAKE_SWIZZLE4(swizzle[0], swizzle[1], swizzle[2], swizzle[3]);
+
+ this->result = src;
+}
+
+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 = src_reg(entry->file, entry->index, var->type);
+}
+
+void
+ir_to_mesa_visitor::visit(ir_dereference_array *ir)
+{
+ ir_constant *index;
+ src_reg src;
+ int element_size = type_size(ir->type);
+
+ index = ir->array_index->constant_expression_value();
+
+ ir->array->accept(this);
+ src = this->result;
+
+ if (index) {
+ src.index += index->value.i[0] * element_size;
+ } else {
+ /* 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);
+
+ src_reg index_reg;
+
+ if (element_size == 1) {
+ index_reg = this->result;
+ } else {
+ index_reg = get_temp(glsl_type::float_type);
+
+ emit(ir, OPCODE_MUL, dst_reg(index_reg),
+ this->result, src_reg_for_float(element_size));
+ }
+
+ src.reladdr = ralloc(mem_ctx, src_reg);
+ memcpy(src.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.swizzle = swizzle_for_size(ir->type->vector_elements);
+ else
+ src.swizzle = SWIZZLE_NOOP;
+
+ this->result = src;
+}
+
+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 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 dst_reg(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)
+{
+ dst_reg l;
+ 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);
+ src_reg condition = this->result;
+
+ for (i = 0; i < type_size(ir->lhs->type); i++) {
+ if (switch_order) {
+ emit(ir, OPCODE_CMP, l, condition, src_reg(l), r);
+ } else {
+ emit(ir, OPCODE_CMP, l, condition, r, src_reg(l));
+ }
+
+ l.index++;
+ r.index++;
+ }
+ } else {
+ for (i = 0; i < type_size(ir->lhs->type); i++) {
+ emit(ir, OPCODE_MOV, l, r);
+ l.index++;
+ r.index++;
+ }
+ }
+}
+
+
+void
+ir_to_mesa_visitor::visit(ir_constant *ir)
+{
+ src_reg src;
+ 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) {
+ src_reg temp_base = get_temp(ir->type);
+ dst_reg temp = dst_reg(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 = this->result;
+
+ for (i = 0; i < (unsigned int)size; i++) {
+ emit(ir, OPCODE_MOV, temp, src);
+
+ src.index++;
+ temp.index++;
+ }
+ }
+ this->result = temp_base;
+ return;
+ }
+
+ if (ir->type->is_array()) {
+ src_reg temp_base = get_temp(ir->type);
+ dst_reg temp = dst_reg(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 = this->result;
+ for (int j = 0; j < size; j++) {
+ emit(ir, OPCODE_MOV, temp, src);
+
+ src.index++;
+ temp.index++;
+ }
+ }
+ this->result = temp_base;
+ return;
+ }
+
+ if (ir->type->is_matrix()) {
+ src_reg mat = get_temp(ir->type);
+ dst_reg mat_column = dst_reg(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 = src_reg(PROGRAM_CONSTANT, -1, NULL);
+ src.index = _mesa_add_unnamed_constant(this->prog->Parameters,
+ values,
+ ir->type->vector_elements,
+ &src.swizzle);
+ emit(ir, OPCODE_MOV, mat_column, src);
+
+ mat_column.index++;
+ }
+
+ this->result = mat;
+ return;
+ }
+
+ src.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 = 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 = undef_src;
+ }
+
+ 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);
+ src_reg r = this->result;
+
+ 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++) {
+ emit(ir, OPCODE_MOV, l, r);
+ l.index++;
+ r.index++;
+ }
+ }
+
+ sig_iter.next();
+ }
+ assert(!sig_iter.has_next());
+
+ /* Emit call instruction */
+ call_inst = emit(ir, OPCODE_CAL);
+ 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);
+
+ 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);
+ dst_reg l = dst_reg(this->result);
+
+ for (i = 0; i < type_size(param->type); i++) {
+ emit(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)
+{
+ src_reg result_src, coord, lod_info, projector, dx, dy;
+ 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 = dst_reg(coord);
+ emit(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 = dst_reg(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:
+ opcode = OPCODE_TXD;
+ ir->lod_info.grad.dPdx->accept(this);
+ dx = this->result;
+ ir->lod_info.grad.dPdy->accept(this);
+ dy = this->result;
+ break;
+ 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;
+ emit(ir, OPCODE_MOV, coord_dst, projector);
+ coord_dst.writemask = WRITEMASK_XYZW;
+ opcode = OPCODE_TXP;
+ } else {
+ 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;
+ emit(ir, OPCODE_RCP, coord_dst, projector);
+
+ /* In the case where we have to project the coordinates "by hand,"
+ * the shadow comparitor value must also be projected.
+ */
+ src_reg tmp_src = coord;
+ if (ir->shadow_comparitor) {
+ /* Slot the shadow value in as the second to last component of the
+ * coord.
+ */
+ ir->shadow_comparitor->accept(this);
+
+ tmp_src = get_temp(glsl_type::vec4_type);
+ dst_reg tmp_dst = dst_reg(tmp_src);
+
+ tmp_dst.writemask = WRITEMASK_Z;
+ emit(ir, OPCODE_MOV, tmp_dst, this->result);
+
+ tmp_dst.writemask = WRITEMASK_XY;
+ emit(ir, OPCODE_MOV, tmp_dst, coord);
+ }
+
+ coord_dst.writemask = WRITEMASK_XYZ;
+ emit(ir, OPCODE_MUL, coord_dst, tmp_src, coord_w);
+
+ coord_dst.writemask = WRITEMASK_XYZW;
+ coord.swizzle = SWIZZLE_XYZW;
+ }
+ }
+
+ /* If projection is done and the opcode is not OPCODE_TXP, then the shadow
+ * comparitor was put in the correct place (and projected) by the code,
+ * above, that handles by-hand projection.
+ */
+ if (ir->shadow_comparitor && (!ir->projector || opcode == OPCODE_TXP)) {
+ /* Slot the shadow value in as the second to last component of the
+ * coord.
+ */
+ ir->shadow_comparitor->accept(this);
+ coord_dst.writemask = WRITEMASK_Z;
+ emit(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;
+ emit(ir, OPCODE_MOV, coord_dst, lod_info);
+ coord_dst.writemask = WRITEMASK_XYZW;
+ }
+
+ if (opcode == OPCODE_TXD)
+ inst = emit(ir, opcode, result_dst, coord, dx, dy);
+ else
+ inst = emit(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()) {
+ dst_reg l;
+ int i;
+
+ assert(current_function);
+
+ ir->get_value()->accept(this);
+ src_reg r = this->result;
+
+ l = dst_reg(current_function->return_reg);
+
+ for (i = 0; i < type_size(current_function->sig->return_type); i++) {
+ emit(ir, OPCODE_MOV, l, r);
+ l.index++;
+ r.index++;
+ }
+ }
+
+ emit(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;
+ emit(ir, OPCODE_KIL, undef_dst, this->result);
+ } else {
+ emit(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;
+ 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) {
+ src_reg temp = get_temp(glsl_type::bool_type);
+ cond_inst = emit(ir->condition, OPCODE_MOV, dst_reg(temp), result);
+ }
+ cond_inst->cond_update = GL_TRUE;
+
+ if_inst = emit(ir->condition, OPCODE_IF);
+ if_inst->dst.cond_mask = COND_NE;
+ } else {
+ if_inst = emit(ir->condition, OPCODE_IF, undef_dst, this->result);
+ }
+
+ this->instructions.push_tail(if_inst);
+
+ visit_exec_list(&ir->then_instructions, this);
+
+ if (!ir->else_instructions.is_empty()) {
+ emit(ir->condition, OPCODE_ELSE);
+ visit_exec_list(&ir->else_instructions, this);
+ }
+
+ if_inst = emit(ir->condition, OPCODE_ENDIF);
+}
+
+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(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.file != PROGRAM_TEMPORARY
+ || inst->dst.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[r].index * 4;
+
+ if (inst->src[r].file != PROGRAM_TEMPORARY ||
+ inst->src[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[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[0].file != copy_chan->src[0].file ||
+ first->src[0].index != copy_chan->src[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[r].file = first->src[0].file;
+ inst->src[r].index = first->src[0].index;
+
+ int swizzle = 0;
+ for (int i = 0; i < 4; i++) {
+ int src_chan = GET_SWZ(inst->src[r].swizzle, i);
+ ir_to_mesa_instruction *copy_inst = acp[acp_base + src_chan];
+ swizzle |= (GET_SWZ(copy_inst->src[0].swizzle, src_chan) <<
+ (3 * i));
+ }
+ inst->src[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.file == PROGRAM_TEMPORARY && inst->dst.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.file == PROGRAM_OUTPUT &&
+ inst->dst.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[0].file == PROGRAM_OUTPUT)
+ acp[4 * r + c] = NULL;
+ }
+ }
+ } else if (inst->dst.file == PROGRAM_TEMPORARY ||
+ inst->dst.file == PROGRAM_OUTPUT) {
+ /* Clear where it's used as dst. */
+ if (inst->dst.file == PROGRAM_TEMPORARY) {
+ for (int c = 0; c < 4; c++) {
+ if (inst->dst.writemask & (1 << c)) {
+ acp[4 * inst->dst.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[0].swizzle, c);
+
+ if (acp[4 * r + c]->src[0].file == inst->dst.file &&
+ acp[4 * r + c]->src[0].index == inst->dst.index &&
+ inst->dst.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.file == PROGRAM_TEMPORARY &&
+ !inst->dst.reladdr &&
+ !inst->saturate &&
+ !inst->src[0].reladdr &&
+ !inst->src[0].negate) {
+ for (int i = 0; i < 4; i++) {
+ if (inst->dst.writemask & (1 << i)) {
+ acp[4 * inst->dst.index + i] = inst;
+ acp_level[4 * inst->dst.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.emit(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.emit(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.emit(NULL, OPCODE_RET);
+
+ ir_to_mesa_instruction *end;
+ end = v.emit(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.file;
+ mesa_inst->DstReg.Index = inst->dst.index;
+ mesa_inst->DstReg.CondMask = inst->dst.cond_mask;
+ mesa_inst->DstReg.WriteMask = inst->dst.writemask;
+ mesa_inst->DstReg.RelAddr = inst->dst.reladdr != NULL;
+ mesa_inst->SrcReg[0] = mesa_src_reg_from_ir_src_reg(inst->src[0]);
+ mesa_inst->SrcReg[1] = mesa_src_reg_from_ir_src_reg(inst->src[1]);
+ mesa_inst->SrcReg[2] = mesa_src_reg_from_ir_src_reg(inst->src[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_instruction.h b/mesalib/src/mesa/program/prog_instruction.h index db2b594e7..19e9b95a3 100644 --- a/mesalib/src/mesa/program/prog_instruction.h +++ b/mesalib/src/mesa/program/prog_instruction.h @@ -1,454 +1,454 @@ -/* - * Mesa 3-D graphics library - * Version: 7.3 - * - * 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. - */ - - -/** - * \file prog_instruction.h - * - * Vertex/fragment program instruction datatypes and constants. - * - * \author Brian Paul - * \author Keith Whitwell - * \author Ian Romanick <idr@us.ibm.com> - */ - - -#ifndef PROG_INSTRUCTION_H -#define PROG_INSTRUCTION_H - - -#include "main/glheader.h" - - -/** - * Swizzle indexes. - * Do not change! - */ -/*@{*/ -#define SWIZZLE_X 0 -#define SWIZZLE_Y 1 -#define SWIZZLE_Z 2 -#define SWIZZLE_W 3 -#define SWIZZLE_ZERO 4 /**< For SWZ instruction only */ -#define SWIZZLE_ONE 5 /**< For SWZ instruction only */ -#define SWIZZLE_NIL 7 /**< used during shader code gen (undefined value) */ -/*@}*/ - -#define MAKE_SWIZZLE4(a,b,c,d) (((a)<<0) | ((b)<<3) | ((c)<<6) | ((d)<<9)) -#define SWIZZLE_NOOP MAKE_SWIZZLE4(0,1,2,3) -#define GET_SWZ(swz, idx) (((swz) >> ((idx)*3)) & 0x7) -#define GET_BIT(msk, idx) (((msk) >> (idx)) & 0x1) - -#define SWIZZLE_XYZW MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_W) -#define SWIZZLE_XXXX MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_X, SWIZZLE_X, SWIZZLE_X) -#define SWIZZLE_YYYY MAKE_SWIZZLE4(SWIZZLE_Y, SWIZZLE_Y, SWIZZLE_Y, SWIZZLE_Y) -#define SWIZZLE_ZZZZ MAKE_SWIZZLE4(SWIZZLE_Z, SWIZZLE_Z, SWIZZLE_Z, SWIZZLE_Z) -#define SWIZZLE_WWWW MAKE_SWIZZLE4(SWIZZLE_W, SWIZZLE_W, SWIZZLE_W, SWIZZLE_W) - - -/** - * Writemask values, 1 bit per component. - */ -/*@{*/ -#define WRITEMASK_X 0x1 -#define WRITEMASK_Y 0x2 -#define WRITEMASK_XY 0x3 -#define WRITEMASK_Z 0x4 -#define WRITEMASK_XZ 0x5 -#define WRITEMASK_YZ 0x6 -#define WRITEMASK_XYZ 0x7 -#define WRITEMASK_W 0x8 -#define WRITEMASK_XW 0x9 -#define WRITEMASK_YW 0xa -#define WRITEMASK_XYW 0xb -#define WRITEMASK_ZW 0xc -#define WRITEMASK_XZW 0xd -#define WRITEMASK_YZW 0xe -#define WRITEMASK_XYZW 0xf -/*@}*/ - - -/** - * Condition codes - */ -/*@{*/ -#define COND_GT 1 /**< greater than zero */ -#define COND_EQ 2 /**< equal to zero */ -#define COND_LT 3 /**< less than zero */ -#define COND_UN 4 /**< unordered (NaN) */ -#define COND_GE 5 /**< greater than or equal to zero */ -#define COND_LE 6 /**< less than or equal to zero */ -#define COND_NE 7 /**< not equal to zero */ -#define COND_TR 8 /**< always true */ -#define COND_FL 9 /**< always false */ -/*@}*/ - - -/** - * Instruction precision for GL_NV_fragment_program - */ -/*@{*/ -#define FLOAT32 0x1 -#define FLOAT16 0x2 -#define FIXED12 0x4 -/*@}*/ - - -/** - * Saturation modes when storing values. - */ -/*@{*/ -#define SATURATE_OFF 0 -#define SATURATE_ZERO_ONE 1 -/*@}*/ - - -/** - * Per-component negation masks - */ -/*@{*/ -#define NEGATE_X 0x1 -#define NEGATE_Y 0x2 -#define NEGATE_Z 0x4 -#define NEGATE_W 0x8 -#define NEGATE_XYZ 0x7 -#define NEGATE_XYZW 0xf -#define NEGATE_NONE 0x0 -/*@}*/ - - -/** - * Program instruction opcodes for vertex, fragment and geometry programs. - */ -typedef enum prog_opcode { - /* ARB_vp ARB_fp NV_vp NV_fp GLSL */ - /*------------------------------------------*/ - OPCODE_NOP = 0, /* X */ - OPCODE_ABS, /* X X 1.1 X */ - OPCODE_ADD, /* X X X X X */ - OPCODE_AND, /* */ - OPCODE_ARA, /* 2 */ - OPCODE_ARL, /* X X X */ - OPCODE_ARL_NV, /* 2 */ - OPCODE_ARR, /* 2 */ - OPCODE_BGNLOOP, /* opt */ - OPCODE_BGNSUB, /* opt */ - OPCODE_BRA, /* 2 */ - OPCODE_BRK, /* 2 opt */ - OPCODE_CAL, /* 2 2 opt */ - OPCODE_CMP, /* X X */ - OPCODE_CONT, /* opt */ - OPCODE_COS, /* X 2 X X */ - OPCODE_DDX, /* X X */ - OPCODE_DDY, /* X X */ - OPCODE_DP2, /* 2 X */ - OPCODE_DP2A, /* 2 */ - OPCODE_DP3, /* X X X X X */ - OPCODE_DP4, /* X X X X X */ - OPCODE_DPH, /* X X 1.1 */ - OPCODE_DST, /* X X X X */ - OPCODE_ELSE, /* opt */ - OPCODE_EMIT_VERTEX,/* X */ - OPCODE_END, /* X X X X opt */ - OPCODE_END_PRIMITIVE,/* X */ - OPCODE_ENDIF, /* opt */ - OPCODE_ENDLOOP, /* opt */ - OPCODE_ENDSUB, /* opt */ - OPCODE_EX2, /* X X 2 X X */ - OPCODE_EXP, /* X X */ - OPCODE_FLR, /* X X 2 X X */ - OPCODE_FRC, /* X X 2 X X */ - OPCODE_IF, /* opt */ - OPCODE_KIL, /* X X */ - OPCODE_KIL_NV, /* X X */ - OPCODE_LG2, /* X X 2 X X */ - OPCODE_LIT, /* X X X X */ - OPCODE_LOG, /* X X */ - OPCODE_LRP, /* X X */ - OPCODE_MAD, /* X X X X X */ - OPCODE_MAX, /* X X X X X */ - OPCODE_MIN, /* X X X X X */ - OPCODE_MOV, /* X X X X X */ - OPCODE_MUL, /* X X X X X */ - OPCODE_NOISE1, /* X */ - OPCODE_NOISE2, /* X */ - OPCODE_NOISE3, /* X */ - OPCODE_NOISE4, /* X */ - OPCODE_NOT, /* */ - OPCODE_NRM3, /* */ - OPCODE_NRM4, /* */ - OPCODE_OR, /* */ - OPCODE_PK2H, /* X */ - OPCODE_PK2US, /* X */ - OPCODE_PK4B, /* X */ - OPCODE_PK4UB, /* X */ - OPCODE_POW, /* X X X X */ - OPCODE_POPA, /* 3 */ - OPCODE_PRINT, /* X X */ - OPCODE_PUSHA, /* 3 */ - OPCODE_RCC, /* 1.1 */ - OPCODE_RCP, /* X X X X X */ - OPCODE_RET, /* 2 2 opt */ - OPCODE_RFL, /* X X */ - OPCODE_RSQ, /* X X X X X */ - OPCODE_SCS, /* X X */ - OPCODE_SEQ, /* 2 X X */ - OPCODE_SFL, /* 2 X */ - OPCODE_SGE, /* X X X X X */ - OPCODE_SGT, /* 2 X X */ - OPCODE_SIN, /* X 2 X X */ - OPCODE_SLE, /* 2 X X */ - OPCODE_SLT, /* X X X X X */ - OPCODE_SNE, /* 2 X X */ - OPCODE_SSG, /* 2 X */ - OPCODE_STR, /* 2 X */ - OPCODE_SUB, /* X X 1.1 X X */ - OPCODE_SWZ, /* X X X */ - OPCODE_TEX, /* X 3 X X */ - OPCODE_TXB, /* X 3 X */ - OPCODE_TXD, /* X X */ - OPCODE_TXL, /* 3 2 X */ - OPCODE_TXP, /* X X */ - OPCODE_TXP_NV, /* 3 X */ - OPCODE_TRUNC, /* X */ - OPCODE_UP2H, /* X */ - OPCODE_UP2US, /* X */ - OPCODE_UP4B, /* X */ - OPCODE_UP4UB, /* X */ - OPCODE_X2D, /* X */ - OPCODE_XOR, /* */ - OPCODE_XPD, /* X X */ - MAX_OPCODE -} gl_inst_opcode; - - -/** - * Number of bits for the src/dst register Index field. - * This limits the size of temp/uniform register files. - */ -#define INST_INDEX_BITS 12 - - -/** - * Instruction source register. - */ -struct prog_src_register -{ - GLuint File:4; /**< One of the PROGRAM_* register file values. */ - GLint Index:(INST_INDEX_BITS+1); /**< Extra bit here for sign bit. - * May be negative for relative addressing. - */ - GLuint Swizzle:12; - GLuint RelAddr:1; - - /** Take the component-wise absolute value */ - GLuint Abs:1; - - /** - * Post-Abs negation. - * This will either be NEGATE_NONE or NEGATE_XYZW, except for the SWZ - * instruction which allows per-component negation. - */ - GLuint Negate:4; - - /** - * Is the register two-dimensional. - * Two dimensional registers are of the - * REGISTER[index][index2] format. - * They are used by the geometry shaders where - * the first index is the index within an array - * and the second index is the semantic of the - * array, e.g. gl_PositionIn[index] would become - * INPUT[index][gl_PositionIn] - */ - GLuint HasIndex2:1; - GLuint RelAddr2:1; - GLint Index2:(INST_INDEX_BITS+1); /**< Extra bit here for sign bit. - * May be negative for relative - * addressing. */ -}; - - -/** - * Instruction destination register. - */ -struct prog_dst_register -{ - GLuint File:4; /**< One of the PROGRAM_* register file values */ - GLuint Index:INST_INDEX_BITS; /**< Unsigned, never negative */ - GLuint WriteMask:4; - GLuint RelAddr:1; - - /** - * \name Conditional destination update control. - * - * \since - * NV_fragment_program, NV_fragment_program_option, NV_vertex_program2, - * NV_vertex_program2_option. - */ - /*@{*/ - /** - * Takes one of the 9 possible condition values (EQ, FL, GT, GE, LE, LT, - * NE, TR, or UN). Dest reg is only written to if the matching - * (swizzled) condition code value passes. When a conditional update mask - * is not specified, this will be \c COND_TR. - */ - GLuint CondMask:4; - - /** - * Condition code swizzle value. - */ - GLuint CondSwizzle:12; - - /** - * Selects the condition code register to use for conditional destination - * update masking. In NV_fragmnet_program or NV_vertex_program2 mode, only - * condition code register 0 is available. In NV_vertex_program3 mode, - * condition code registers 0 and 1 are available. - */ - GLuint CondSrc:1; - /*@}*/ -}; - - -/** - * Vertex/fragment program instruction. - */ -struct prog_instruction -{ - gl_inst_opcode Opcode; - struct prog_src_register SrcReg[3]; - struct prog_dst_register DstReg; - - /** - * Indicates that the instruction should update the condition code - * register. - * - * \since - * NV_fragment_program, NV_fragment_program_option, NV_vertex_program2, - * NV_vertex_program2_option. - */ - GLuint CondUpdate:1; - - /** - * If prog_instruction::CondUpdate is \c GL_TRUE, this value selects the - * condition code register that is to be updated. - * - * In GL_NV_fragment_program or GL_NV_vertex_program2 mode, only condition - * code register 0 is available. In GL_NV_vertex_program3 mode, condition - * code registers 0 and 1 are available. - * - * \since - * NV_fragment_program, NV_fragment_program_option, NV_vertex_program2, - * NV_vertex_program2_option. - */ - GLuint CondDst:1; - - /** - * Saturate each value of the vectored result to the range [0,1] or the - * range [-1,1]. \c SSAT mode (i.e., saturation to the range [-1,1]) is - * only available in NV_fragment_program2 mode. - * Value is one of the SATURATE_* tokens. - * - * \since - * NV_fragment_program, NV_fragment_program_option, NV_vertex_program3. - */ - GLuint SaturateMode:2; - - /** - * Per-instruction selectable precision: FLOAT32, FLOAT16, FIXED12. - * - * \since - * NV_fragment_program, NV_fragment_program_option. - */ - GLuint Precision:3; - - /** - * \name Extra fields for TEX, TXB, TXD, TXL, TXP instructions. - */ - /*@{*/ - /** Source texture unit. */ - GLuint TexSrcUnit:5; - - /** Source texture target, one of TEXTURE_{1D,2D,3D,CUBE,RECT}_INDEX */ - GLuint TexSrcTarget:3; - - /** True if tex instruction should do shadow comparison */ - GLuint TexShadow:1; - /*@}*/ - - /** - * For BRA and CAL instructions, the location to jump to. - * For BGNLOOP, points to ENDLOOP (and vice-versa). - * For BRK, points to ENDLOOP - * For IF, points to ELSE or ENDIF. - * For ELSE, points to ENDIF. - */ - GLint BranchTarget; - - /** for debugging purposes */ - const char *Comment; - - /** Arbitrary data. Used for OPCODE_PRINT and some drivers */ - void *Data; - - /** for driver use (try to remove someday) */ - GLint Aux; -}; - - -extern void -_mesa_init_instructions(struct prog_instruction *inst, GLuint count); - -extern struct prog_instruction * -_mesa_alloc_instructions(GLuint numInst); - -extern struct prog_instruction * -_mesa_realloc_instructions(struct prog_instruction *oldInst, - GLuint numOldInst, GLuint numNewInst); - -extern struct prog_instruction * -_mesa_copy_instructions(struct prog_instruction *dest, - const struct prog_instruction *src, GLuint n); - -extern void -_mesa_free_instructions(struct prog_instruction *inst, GLuint count); - -extern GLuint -_mesa_num_inst_src_regs(gl_inst_opcode opcode); - -extern GLuint -_mesa_num_inst_dst_regs(gl_inst_opcode opcode); - -extern GLboolean -_mesa_is_tex_instruction(gl_inst_opcode opcode); - -extern GLboolean -_mesa_check_soa_dependencies(const struct prog_instruction *inst); - -extern const char * -_mesa_opcode_string(gl_inst_opcode opcode); - - -#endif /* PROG_INSTRUCTION_H */ +/*
+ * Mesa 3-D graphics library
+ * Version: 7.3
+ *
+ * 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.
+ */
+
+
+/**
+ * \file prog_instruction.h
+ *
+ * Vertex/fragment program instruction datatypes and constants.
+ *
+ * \author Brian Paul
+ * \author Keith Whitwell
+ * \author Ian Romanick <idr@us.ibm.com>
+ */
+
+
+#ifndef PROG_INSTRUCTION_H
+#define PROG_INSTRUCTION_H
+
+
+#include "main/glheader.h"
+
+
+/**
+ * Swizzle indexes.
+ * Do not change!
+ */
+/*@{*/
+#define SWIZZLE_X 0
+#define SWIZZLE_Y 1
+#define SWIZZLE_Z 2
+#define SWIZZLE_W 3
+#define SWIZZLE_ZERO 4 /**< For SWZ instruction only */
+#define SWIZZLE_ONE 5 /**< For SWZ instruction only */
+#define SWIZZLE_NIL 7 /**< used during shader code gen (undefined value) */
+/*@}*/
+
+#define MAKE_SWIZZLE4(a,b,c,d) (((a)<<0) | ((b)<<3) | ((c)<<6) | ((d)<<9))
+#define SWIZZLE_NOOP MAKE_SWIZZLE4(0,1,2,3)
+#define GET_SWZ(swz, idx) (((swz) >> ((idx)*3)) & 0x7)
+#define GET_BIT(msk, idx) (((msk) >> (idx)) & 0x1)
+
+#define SWIZZLE_XYZW MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_W)
+#define SWIZZLE_XXXX MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_X, SWIZZLE_X, SWIZZLE_X)
+#define SWIZZLE_YYYY MAKE_SWIZZLE4(SWIZZLE_Y, SWIZZLE_Y, SWIZZLE_Y, SWIZZLE_Y)
+#define SWIZZLE_ZZZZ MAKE_SWIZZLE4(SWIZZLE_Z, SWIZZLE_Z, SWIZZLE_Z, SWIZZLE_Z)
+#define SWIZZLE_WWWW MAKE_SWIZZLE4(SWIZZLE_W, SWIZZLE_W, SWIZZLE_W, SWIZZLE_W)
+
+
+/**
+ * Writemask values, 1 bit per component.
+ */
+/*@{*/
+#define WRITEMASK_X 0x1
+#define WRITEMASK_Y 0x2
+#define WRITEMASK_XY 0x3
+#define WRITEMASK_Z 0x4
+#define WRITEMASK_XZ 0x5
+#define WRITEMASK_YZ 0x6
+#define WRITEMASK_XYZ 0x7
+#define WRITEMASK_W 0x8
+#define WRITEMASK_XW 0x9
+#define WRITEMASK_YW 0xa
+#define WRITEMASK_XYW 0xb
+#define WRITEMASK_ZW 0xc
+#define WRITEMASK_XZW 0xd
+#define WRITEMASK_YZW 0xe
+#define WRITEMASK_XYZW 0xf
+/*@}*/
+
+
+/**
+ * Condition codes
+ */
+/*@{*/
+#define COND_GT 1 /**< greater than zero */
+#define COND_EQ 2 /**< equal to zero */
+#define COND_LT 3 /**< less than zero */
+#define COND_UN 4 /**< unordered (NaN) */
+#define COND_GE 5 /**< greater than or equal to zero */
+#define COND_LE 6 /**< less than or equal to zero */
+#define COND_NE 7 /**< not equal to zero */
+#define COND_TR 8 /**< always true */
+#define COND_FL 9 /**< always false */
+/*@}*/
+
+
+/**
+ * Instruction precision for GL_NV_fragment_program
+ */
+/*@{*/
+#define FLOAT32 0x1
+#define FLOAT16 0x2
+#define FIXED12 0x4
+/*@}*/
+
+
+/**
+ * Saturation modes when storing values.
+ */
+/*@{*/
+#define SATURATE_OFF 0
+#define SATURATE_ZERO_ONE 1
+/*@}*/
+
+
+/**
+ * Per-component negation masks
+ */
+/*@{*/
+#define NEGATE_X 0x1
+#define NEGATE_Y 0x2
+#define NEGATE_Z 0x4
+#define NEGATE_W 0x8
+#define NEGATE_XYZ 0x7
+#define NEGATE_XYZW 0xf
+#define NEGATE_NONE 0x0
+/*@}*/
+
+
+/**
+ * Program instruction opcodes for vertex, fragment and geometry programs.
+ */
+typedef enum prog_opcode {
+ /* ARB_vp ARB_fp NV_vp NV_fp GLSL */
+ /*------------------------------------------*/
+ OPCODE_NOP = 0, /* X */
+ OPCODE_ABS, /* X X 1.1 X */
+ OPCODE_ADD, /* X X X X X */
+ OPCODE_AND, /* */
+ OPCODE_ARA, /* 2 */
+ OPCODE_ARL, /* X X X */
+ OPCODE_ARL_NV, /* 2 */
+ OPCODE_ARR, /* 2 */
+ OPCODE_BGNLOOP, /* opt */
+ OPCODE_BGNSUB, /* opt */
+ OPCODE_BRA, /* 2 */
+ OPCODE_BRK, /* 2 opt */
+ OPCODE_CAL, /* 2 2 opt */
+ OPCODE_CMP, /* X X */
+ OPCODE_CONT, /* opt */
+ OPCODE_COS, /* X 2 X X */
+ OPCODE_DDX, /* X X */
+ OPCODE_DDY, /* X X */
+ OPCODE_DP2, /* 2 X */
+ OPCODE_DP2A, /* 2 */
+ OPCODE_DP3, /* X X X X X */
+ OPCODE_DP4, /* X X X X X */
+ OPCODE_DPH, /* X X 1.1 */
+ OPCODE_DST, /* X X X X */
+ OPCODE_ELSE, /* opt */
+ OPCODE_EMIT_VERTEX,/* X */
+ OPCODE_END, /* X X X X opt */
+ OPCODE_END_PRIMITIVE,/* X */
+ OPCODE_ENDIF, /* opt */
+ OPCODE_ENDLOOP, /* opt */
+ OPCODE_ENDSUB, /* opt */
+ OPCODE_EX2, /* X X 2 X X */
+ OPCODE_EXP, /* X X */
+ OPCODE_FLR, /* X X 2 X X */
+ OPCODE_FRC, /* X X 2 X X */
+ OPCODE_IF, /* opt */
+ OPCODE_KIL, /* X X */
+ OPCODE_KIL_NV, /* X X */
+ OPCODE_LG2, /* X X 2 X X */
+ OPCODE_LIT, /* X X X X */
+ OPCODE_LOG, /* X X */
+ OPCODE_LRP, /* X X */
+ OPCODE_MAD, /* X X X X X */
+ OPCODE_MAX, /* X X X X X */
+ OPCODE_MIN, /* X X X X X */
+ OPCODE_MOV, /* X X X X X */
+ OPCODE_MUL, /* X X X X X */
+ OPCODE_NOISE1, /* X */
+ OPCODE_NOISE2, /* X */
+ OPCODE_NOISE3, /* X */
+ OPCODE_NOISE4, /* X */
+ OPCODE_NOT, /* */
+ OPCODE_NRM3, /* */
+ OPCODE_NRM4, /* */
+ OPCODE_OR, /* */
+ OPCODE_PK2H, /* X */
+ OPCODE_PK2US, /* X */
+ OPCODE_PK4B, /* X */
+ OPCODE_PK4UB, /* X */
+ OPCODE_POW, /* X X X X */
+ OPCODE_POPA, /* 3 */
+ OPCODE_PRINT, /* X X */
+ OPCODE_PUSHA, /* 3 */
+ OPCODE_RCC, /* 1.1 */
+ OPCODE_RCP, /* X X X X X */
+ OPCODE_RET, /* 2 2 opt */
+ OPCODE_RFL, /* X X */
+ OPCODE_RSQ, /* X X X X X */
+ OPCODE_SCS, /* X X */
+ OPCODE_SEQ, /* 2 X X */
+ OPCODE_SFL, /* 2 X */
+ OPCODE_SGE, /* X X X X X */
+ OPCODE_SGT, /* 2 X X */
+ OPCODE_SIN, /* X 2 X X */
+ OPCODE_SLE, /* 2 X X */
+ OPCODE_SLT, /* X X X X X */
+ OPCODE_SNE, /* 2 X X */
+ OPCODE_SSG, /* 2 X */
+ OPCODE_STR, /* 2 X */
+ OPCODE_SUB, /* X X 1.1 X X */
+ OPCODE_SWZ, /* X X X */
+ OPCODE_TEX, /* X 3 X X */
+ OPCODE_TXB, /* X 3 X */
+ OPCODE_TXD, /* X X */
+ OPCODE_TXL, /* 3 2 X */
+ OPCODE_TXP, /* X X */
+ OPCODE_TXP_NV, /* 3 X */
+ OPCODE_TRUNC, /* X */
+ OPCODE_UP2H, /* X */
+ OPCODE_UP2US, /* X */
+ OPCODE_UP4B, /* X */
+ OPCODE_UP4UB, /* X */
+ OPCODE_X2D, /* X */
+ OPCODE_XOR, /* */
+ OPCODE_XPD, /* X X */
+ MAX_OPCODE
+} gl_inst_opcode;
+
+
+/**
+ * Number of bits for the src/dst register Index field.
+ * This limits the size of temp/uniform register files.
+ */
+#define INST_INDEX_BITS 12
+
+
+/**
+ * Instruction source register.
+ */
+struct prog_src_register
+{
+ GLuint File:4; /**< One of the PROGRAM_* register file values. */
+ GLint Index:(INST_INDEX_BITS+1); /**< Extra bit here for sign bit.
+ * May be negative for relative addressing.
+ */
+ GLuint Swizzle:12;
+ GLuint RelAddr:1;
+
+ /** Take the component-wise absolute value */
+ GLuint Abs:1;
+
+ /**
+ * Post-Abs negation.
+ * This will either be NEGATE_NONE or NEGATE_XYZW, except for the SWZ
+ * instruction which allows per-component negation.
+ */
+ GLuint Negate:4;
+
+ /**
+ * Is the register two-dimensional.
+ * Two dimensional registers are of the
+ * REGISTER[index][index2] format.
+ * They are used by the geometry shaders where
+ * the first index is the index within an array
+ * and the second index is the semantic of the
+ * array, e.g. gl_PositionIn[index] would become
+ * INPUT[index][gl_PositionIn]
+ */
+ GLuint HasIndex2:1;
+ GLuint RelAddr2:1;
+ GLint Index2:(INST_INDEX_BITS+1); /**< Extra bit here for sign bit.
+ * May be negative for relative
+ * addressing. */
+};
+
+
+/**
+ * Instruction destination register.
+ */
+struct prog_dst_register
+{
+ GLuint File:4; /**< One of the PROGRAM_* register file values */
+ GLuint Index:INST_INDEX_BITS; /**< Unsigned, never negative */
+ GLuint WriteMask:4;
+ GLuint RelAddr:1;
+
+ /**
+ * \name Conditional destination update control.
+ *
+ * \since
+ * NV_fragment_program, NV_fragment_program_option, NV_vertex_program2,
+ * NV_vertex_program2_option.
+ */
+ /*@{*/
+ /**
+ * Takes one of the 9 possible condition values (EQ, FL, GT, GE, LE, LT,
+ * NE, TR, or UN). Dest reg is only written to if the matching
+ * (swizzled) condition code value passes. When a conditional update mask
+ * is not specified, this will be \c COND_TR.
+ */
+ GLuint CondMask:4;
+
+ /**
+ * Condition code swizzle value.
+ */
+ GLuint CondSwizzle:12;
+
+ /**
+ * Selects the condition code register to use for conditional destination
+ * update masking. In NV_fragmnet_program or NV_vertex_program2 mode, only
+ * condition code register 0 is available. In NV_vertex_program3 mode,
+ * condition code registers 0 and 1 are available.
+ */
+ GLuint CondSrc:1;
+ /*@}*/
+};
+
+
+/**
+ * Vertex/fragment program instruction.
+ */
+struct prog_instruction
+{
+ gl_inst_opcode Opcode;
+ struct prog_src_register SrcReg[3];
+ struct prog_dst_register DstReg;
+
+ /**
+ * Indicates that the instruction should update the condition code
+ * register.
+ *
+ * \since
+ * NV_fragment_program, NV_fragment_program_option, NV_vertex_program2,
+ * NV_vertex_program2_option.
+ */
+ GLuint CondUpdate:1;
+
+ /**
+ * If prog_instruction::CondUpdate is \c GL_TRUE, this value selects the
+ * condition code register that is to be updated.
+ *
+ * In GL_NV_fragment_program or GL_NV_vertex_program2 mode, only condition
+ * code register 0 is available. In GL_NV_vertex_program3 mode, condition
+ * code registers 0 and 1 are available.
+ *
+ * \since
+ * NV_fragment_program, NV_fragment_program_option, NV_vertex_program2,
+ * NV_vertex_program2_option.
+ */
+ GLuint CondDst:1;
+
+ /**
+ * Saturate each value of the vectored result to the range [0,1] or the
+ * range [-1,1]. \c SSAT mode (i.e., saturation to the range [-1,1]) is
+ * only available in NV_fragment_program2 mode.
+ * Value is one of the SATURATE_* tokens.
+ *
+ * \since
+ * NV_fragment_program, NV_fragment_program_option, NV_vertex_program3.
+ */
+ GLuint SaturateMode:2;
+
+ /**
+ * Per-instruction selectable precision: FLOAT32, FLOAT16, FIXED12.
+ *
+ * \since
+ * NV_fragment_program, NV_fragment_program_option.
+ */
+ GLuint Precision:3;
+
+ /**
+ * \name Extra fields for TEX, TXB, TXD, TXL, TXP instructions.
+ */
+ /*@{*/
+ /** Source texture unit. */
+ GLuint TexSrcUnit:5;
+
+ /** Source texture target, one of TEXTURE_{1D,2D,3D,CUBE,RECT}_INDEX */
+ GLuint TexSrcTarget:3;
+
+ /** True if tex instruction should do shadow comparison */
+ GLuint TexShadow:1;
+ /*@}*/
+
+ /**
+ * For BRA and CAL instructions, the location to jump to.
+ * For BGNLOOP, points to ENDLOOP (and vice-versa).
+ * For BRK, points to ENDLOOP
+ * For IF, points to ELSE or ENDIF.
+ * For ELSE, points to ENDIF.
+ */
+ GLint BranchTarget;
+
+ /** for debugging purposes */
+ const char *Comment;
+
+ /** Arbitrary data. Used for OPCODE_PRINT and some drivers */
+ void *Data;
+
+ /** for driver use (try to remove someday) */
+ GLint Aux;
+};
+
+
+extern void
+_mesa_init_instructions(struct prog_instruction *inst, GLuint count);
+
+extern struct prog_instruction *
+_mesa_alloc_instructions(GLuint numInst);
+
+extern struct prog_instruction *
+_mesa_realloc_instructions(struct prog_instruction *oldInst,
+ GLuint numOldInst, GLuint numNewInst);
+
+extern struct prog_instruction *
+_mesa_copy_instructions(struct prog_instruction *dest,
+ const struct prog_instruction *src, GLuint n);
+
+extern void
+_mesa_free_instructions(struct prog_instruction *inst, GLuint count);
+
+extern GLuint
+_mesa_num_inst_src_regs(gl_inst_opcode opcode);
+
+extern GLuint
+_mesa_num_inst_dst_regs(gl_inst_opcode opcode);
+
+extern GLboolean
+_mesa_is_tex_instruction(gl_inst_opcode opcode);
+
+extern GLboolean
+_mesa_check_soa_dependencies(const struct prog_instruction *inst);
+
+extern const char *
+_mesa_opcode_string(gl_inst_opcode opcode);
+
+
+#endif /* PROG_INSTRUCTION_H */
diff --git a/mesalib/src/mesa/program/prog_optimize.c b/mesalib/src/mesa/program/prog_optimize.c index 11debc485..8d95402bb 100644 --- a/mesalib/src/mesa/program/prog_optimize.c +++ b/mesalib/src/mesa/program/prog_optimize.c @@ -1,1350 +1,1350 @@ -/* - * Mesa 3-D graphics library - * Version: 7.5 - * - * Copyright (C) 2009 VMware, Inc. 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 - * VMWARE 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. - */ - - - -#include "main/glheader.h" -#include "main/context.h" -#include "main/macros.h" -#include "program.h" -#include "prog_instruction.h" -#include "prog_optimize.h" -#include "prog_print.h" - - -#define MAX_LOOP_NESTING 50 -/* MAX_PROGRAM_TEMPS is a low number (256), and we want to be able to - * register allocate many temporary values into that small number of - * temps. So allow large temporary indices coming into the register - * allocator. - */ -#define REG_ALLOCATE_MAX_PROGRAM_TEMPS ((1 << INST_INDEX_BITS) - 1) - -static GLboolean dbg = GL_FALSE; - -#define NO_MASK 0xf - -/** - * Returns the mask of channels (bitmask of WRITEMASK_X,Y,Z,W) which - * are read from the given src in this instruction, We also provide - * one optional masks which may mask other components in the dst - * register - */ -static GLuint -get_src_arg_mask(const struct prog_instruction *inst, - GLuint arg, GLuint dst_mask) -{ - GLuint read_mask, channel_mask; - GLuint comp; - - ASSERT(arg < _mesa_num_inst_src_regs(inst->Opcode)); - - /* Form the dst register, find the written channels */ - if (inst->CondUpdate) { - channel_mask = WRITEMASK_XYZW; - } - else { - switch (inst->Opcode) { - case OPCODE_MOV: - case OPCODE_MIN: - case OPCODE_MAX: - case OPCODE_ABS: - case OPCODE_ADD: - case OPCODE_MAD: - case OPCODE_MUL: - case OPCODE_SUB: - case OPCODE_CMP: - case OPCODE_FLR: - case OPCODE_FRC: - case OPCODE_LRP: - case OPCODE_SEQ: - case OPCODE_SGE: - case OPCODE_SGT: - case OPCODE_SLE: - case OPCODE_SLT: - case OPCODE_SNE: - case OPCODE_SSG: - channel_mask = inst->DstReg.WriteMask & dst_mask; - break; - case OPCODE_RCP: - case OPCODE_SIN: - case OPCODE_COS: - case OPCODE_RSQ: - case OPCODE_POW: - case OPCODE_EX2: - case OPCODE_LOG: - channel_mask = WRITEMASK_X; - break; - case OPCODE_DP2: - channel_mask = WRITEMASK_XY; - break; - case OPCODE_DP3: - case OPCODE_XPD: - channel_mask = WRITEMASK_XYZ; - break; - default: - channel_mask = WRITEMASK_XYZW; - break; - } - } - - /* Now, given the src swizzle and the written channels, find which - * components are actually read - */ - read_mask = 0x0; - for (comp = 0; comp < 4; ++comp) { - const GLuint coord = GET_SWZ(inst->SrcReg[arg].Swizzle, comp); - ASSERT(coord < 4); - if (channel_mask & (1 << comp) && coord <= SWIZZLE_W) - read_mask |= 1 << coord; - } - - return read_mask; -} - - -/** - * For a MOV instruction, compute a write mask when src register also has - * a mask - */ -static GLuint -get_dst_mask_for_mov(const struct prog_instruction *mov, GLuint src_mask) -{ - const GLuint mask = mov->DstReg.WriteMask; - GLuint comp; - GLuint updated_mask = 0x0; - - ASSERT(mov->Opcode == OPCODE_MOV); - - for (comp = 0; comp < 4; ++comp) { - GLuint src_comp; - if ((mask & (1 << comp)) == 0) - continue; - src_comp = GET_SWZ(mov->SrcReg[0].Swizzle, comp); - if ((src_mask & (1 << src_comp)) == 0) - continue; - updated_mask |= 1 << comp; - } - - return updated_mask; -} - - -/** - * Ensure that the swizzle is regular. That is, all of the swizzle - * terms are SWIZZLE_X,Y,Z,W and not SWIZZLE_ZERO or SWIZZLE_ONE. - */ -static GLboolean -is_swizzle_regular(GLuint swz) -{ - return GET_SWZ(swz,0) <= SWIZZLE_W && - GET_SWZ(swz,1) <= SWIZZLE_W && - GET_SWZ(swz,2) <= SWIZZLE_W && - GET_SWZ(swz,3) <= SWIZZLE_W; -} - - -/** - * In 'prog' remove instruction[i] if removeFlags[i] == TRUE. - * \return number of instructions removed - */ -static GLuint -remove_instructions(struct gl_program *prog, const GLboolean *removeFlags) -{ - GLint i, removeEnd = 0, removeCount = 0; - GLuint totalRemoved = 0; - - /* go backward */ - for (i = prog->NumInstructions - 1; i >= 0; i--) { - if (removeFlags[i]) { - totalRemoved++; - if (removeCount == 0) { - /* begin a run of instructions to remove */ - removeEnd = i; - removeCount = 1; - } - else { - /* extend the run of instructions to remove */ - removeCount++; - } - } - else { - /* don't remove this instruction, but check if the preceeding - * instructions are to be removed. - */ - if (removeCount > 0) { - GLint removeStart = removeEnd - removeCount + 1; - _mesa_delete_instructions(prog, removeStart, removeCount); - removeStart = removeCount = 0; /* reset removal info */ - } - } - } - /* Finish removing if the first instruction was to be removed. */ - if (removeCount > 0) { - GLint removeStart = removeEnd - removeCount + 1; - _mesa_delete_instructions(prog, removeStart, removeCount); - } - return totalRemoved; -} - - -/** - * Remap register indexes according to map. - * \param prog the program to search/replace - * \param file the type of register file to search/replace - * \param map maps old register indexes to new indexes - */ -static void -replace_regs(struct gl_program *prog, gl_register_file file, const GLint map[]) -{ - GLuint i; - - 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 == file) { - GLuint index = inst->SrcReg[j].Index; - ASSERT(map[index] >= 0); - inst->SrcReg[j].Index = map[index]; - } - } - if (inst->DstReg.File == file) { - const GLuint index = inst->DstReg.Index; - ASSERT(map[index] >= 0); - inst->DstReg.Index = map[index]; - } - } -} - - -/** - * Remove dead instructions from the given program. - * This is very primitive for now. Basically look for temp registers - * that are written to but never read. Remove any instructions that - * write to such registers. Be careful with condition code setters. - */ -static GLboolean -_mesa_remove_dead_code_global(struct gl_program *prog) -{ - GLboolean tempRead[REG_ALLOCATE_MAX_PROGRAM_TEMPS][4]; - GLboolean *removeInst; /* per-instruction removal flag */ - GLuint i, rem = 0, comp; - - memset(tempRead, 0, sizeof(tempRead)); - - if (dbg) { - printf("Optimize: Begin dead code removal\n"); - /*_mesa_print_program(prog);*/ - } - - removeInst = (GLboolean *) - calloc(1, prog->NumInstructions * sizeof(GLboolean)); - - /* Determine which temps are read and written */ - for (i = 0; i < prog->NumInstructions; i++) { - const struct prog_instruction *inst = prog->Instructions + i; - const GLuint numSrc = _mesa_num_inst_src_regs(inst->Opcode); - GLuint j; - - /* check src regs */ - for (j = 0; j < numSrc; j++) { - if (inst->SrcReg[j].File == PROGRAM_TEMPORARY) { - const GLuint index = inst->SrcReg[j].Index; - GLuint read_mask; - ASSERT(index < REG_ALLOCATE_MAX_PROGRAM_TEMPS); - read_mask = get_src_arg_mask(inst, j, NO_MASK); - - if (inst->SrcReg[j].RelAddr) { - if (dbg) - printf("abort remove dead code (indirect temp)\n"); - goto done; - } - - for (comp = 0; comp < 4; comp++) { - const GLuint swz = GET_SWZ(inst->SrcReg[j].Swizzle, comp); - ASSERT(swz < 4); - if ((read_mask & (1 << swz)) == 0) - continue; - if (swz <= SWIZZLE_W) - tempRead[index][swz] = GL_TRUE; - } - } - } - - /* check dst reg */ - if (inst->DstReg.File == PROGRAM_TEMPORARY) { - const GLuint index = inst->DstReg.Index; - ASSERT(index < REG_ALLOCATE_MAX_PROGRAM_TEMPS); - - if (inst->DstReg.RelAddr) { - if (dbg) - printf("abort remove dead code (indirect temp)\n"); - goto done; - } - - if (inst->CondUpdate) { - /* If we're writing to this register and setting condition - * codes we cannot remove the instruction. Prevent removal - * by setting the 'read' flag. - */ - tempRead[index][0] = GL_TRUE; - tempRead[index][1] = GL_TRUE; - tempRead[index][2] = GL_TRUE; - tempRead[index][3] = GL_TRUE; - } - } - } - - /* find instructions that write to dead registers, flag for removal */ - for (i = 0; i < prog->NumInstructions; i++) { - struct prog_instruction *inst = prog->Instructions + i; - const GLuint numDst = _mesa_num_inst_dst_regs(inst->Opcode); - - if (numDst != 0 && inst->DstReg.File == PROGRAM_TEMPORARY) { - GLint chan, index = inst->DstReg.Index; - - for (chan = 0; chan < 4; chan++) { - if (!tempRead[index][chan] && - inst->DstReg.WriteMask & (1 << chan)) { - if (dbg) { - printf("Remove writemask on %u.%c\n", i, - chan == 3 ? 'w' : 'x' + chan); - } - inst->DstReg.WriteMask &= ~(1 << chan); - rem++; - } - } - - if (inst->DstReg.WriteMask == 0) { - /* If we cleared all writes, the instruction can be removed. */ - if (dbg) - printf("Remove instruction %u: \n", i); - removeInst[i] = GL_TRUE; - } - } - } - - /* now remove the instructions which aren't needed */ - rem = remove_instructions(prog, removeInst); - - if (dbg) { - printf("Optimize: End dead code removal.\n"); - printf(" %u channel writes removed\n", rem); - printf(" %u instructions removed\n", rem); - /*_mesa_print_program(prog);*/ - } - -done: - free(removeInst); - return rem != 0; -} - - -enum inst_use -{ - READ, - WRITE, - FLOW, - END -}; - - -/** - * Scan forward in program from 'start' for the next occurances of TEMP[index]. - * We look if an instruction reads the component given by the masks and if they - * are overwritten. - * Return READ, WRITE, FLOW or END to indicate the next usage or an indicator - * that we can't look further. - */ -static enum inst_use -find_next_use(const struct gl_program *prog, - GLuint start, - GLuint index, - GLuint mask) -{ - GLuint i; - - for (i = start; i < prog->NumInstructions; i++) { - const struct prog_instruction *inst = prog->Instructions + i; - switch (inst->Opcode) { - case OPCODE_BGNLOOP: - case OPCODE_BGNSUB: - case OPCODE_BRA: - case OPCODE_CAL: - case OPCODE_CONT: - case OPCODE_IF: - case OPCODE_ELSE: - case OPCODE_ENDIF: - case OPCODE_ENDLOOP: - case OPCODE_ENDSUB: - case OPCODE_RET: - return FLOW; - case OPCODE_END: - return END; - default: - { - const GLuint numSrc = _mesa_num_inst_src_regs(inst->Opcode); - GLuint j; - for (j = 0; j < numSrc; j++) { - if (inst->SrcReg[j].RelAddr || - (inst->SrcReg[j].File == PROGRAM_TEMPORARY && - inst->SrcReg[j].Index == index && - (get_src_arg_mask(inst,j,NO_MASK) & mask))) - return READ; - } - if (_mesa_num_inst_dst_regs(inst->Opcode) == 1 && - inst->DstReg.File == PROGRAM_TEMPORARY && - inst->DstReg.Index == index) { - mask &= ~inst->DstReg.WriteMask; - if (mask == 0) - return WRITE; - } - } - } - } - return END; -} - - -/** - * Is the given instruction opcode a flow-control opcode? - * XXX maybe move this into prog_instruction.[ch] - */ -static GLboolean -_mesa_is_flow_control_opcode(enum prog_opcode opcode) -{ - switch (opcode) { - case OPCODE_BGNLOOP: - case OPCODE_BGNSUB: - case OPCODE_BRA: - case OPCODE_CAL: - case OPCODE_CONT: - case OPCODE_IF: - case OPCODE_ELSE: - case OPCODE_END: - case OPCODE_ENDIF: - case OPCODE_ENDLOOP: - case OPCODE_ENDSUB: - case OPCODE_RET: - return GL_TRUE; - default: - return GL_FALSE; - } -} - - -/** - * Test if the given instruction is a simple MOV (no conditional updating, - * not relative addressing, no negation/abs, etc). - */ -static GLboolean -can_downward_mov_be_modifed(const struct prog_instruction *mov) -{ - return - mov->Opcode == OPCODE_MOV && - mov->CondUpdate == GL_FALSE && - mov->SrcReg[0].RelAddr == 0 && - mov->SrcReg[0].Negate == 0 && - mov->SrcReg[0].Abs == 0 && - mov->SrcReg[0].HasIndex2 == 0 && - mov->SrcReg[0].RelAddr2 == 0 && - mov->DstReg.RelAddr == 0 && - mov->DstReg.CondMask == COND_TR && - mov->SaturateMode == SATURATE_OFF; -} - - -static GLboolean -can_upward_mov_be_modifed(const struct prog_instruction *mov) -{ - return - can_downward_mov_be_modifed(mov) && - mov->DstReg.File == PROGRAM_TEMPORARY; -} - - -/** - * Try to remove use of extraneous MOV instructions, to free them up for dead - * code removal. - */ -static void -_mesa_remove_extra_move_use(struct gl_program *prog) -{ - GLuint i, j; - - if (dbg) { - printf("Optimize: Begin remove extra move use\n"); - _mesa_print_program(prog); - } - - /* - * Look for sequences such as this: - * MOV tmpX, arg0; - * ... - * FOO tmpY, tmpX, arg1; - * and convert into: - * MOV tmpX, arg0; - * ... - * FOO tmpY, arg0, arg1; - */ - - for (i = 0; i + 1 < prog->NumInstructions; i++) { - const struct prog_instruction *mov = prog->Instructions + i; - GLuint dst_mask, src_mask; - if (can_upward_mov_be_modifed(mov) == GL_FALSE) - continue; - - /* Scanning the code, we maintain the components which are still active in - * these two masks - */ - dst_mask = mov->DstReg.WriteMask; - src_mask = get_src_arg_mask(mov, 0, NO_MASK); - - /* Walk through remaining instructions until the or src reg gets - * rewritten or we get into some flow-control, eliminating the use of - * this MOV. - */ - for (j = i + 1; j < prog->NumInstructions; j++) { - struct prog_instruction *inst2 = prog->Instructions + j; - GLuint arg; - - if (_mesa_is_flow_control_opcode(inst2->Opcode)) - break; - - /* First rewrite this instruction's args if appropriate. */ - for (arg = 0; arg < _mesa_num_inst_src_regs(inst2->Opcode); arg++) { - GLuint comp, read_mask; - - if (inst2->SrcReg[arg].File != mov->DstReg.File || - inst2->SrcReg[arg].Index != mov->DstReg.Index || - inst2->SrcReg[arg].RelAddr || - inst2->SrcReg[arg].Abs) - continue; - read_mask = get_src_arg_mask(inst2, arg, NO_MASK); - - /* Adjust the swizzles of inst2 to point at MOV's source if ALL the - * components read still come from the mov instructions - */ - if (is_swizzle_regular(inst2->SrcReg[arg].Swizzle) && - (read_mask & dst_mask) == read_mask) { - for (comp = 0; comp < 4; comp++) { - const GLuint inst2_swz = - GET_SWZ(inst2->SrcReg[arg].Swizzle, comp); - const GLuint s = GET_SWZ(mov->SrcReg[0].Swizzle, inst2_swz); - inst2->SrcReg[arg].Swizzle &= ~(7 << (3 * comp)); - inst2->SrcReg[arg].Swizzle |= s << (3 * comp); - inst2->SrcReg[arg].Negate ^= (((mov->SrcReg[0].Negate >> - inst2_swz) & 0x1) << comp); - } - inst2->SrcReg[arg].File = mov->SrcReg[0].File; - inst2->SrcReg[arg].Index = mov->SrcReg[0].Index; - } - } - - /* The source of MOV is written. This potentially deactivates some - * components from the src and dst of the MOV instruction - */ - if (inst2->DstReg.File == mov->DstReg.File && - (inst2->DstReg.RelAddr || - inst2->DstReg.Index == mov->DstReg.Index)) { - dst_mask &= ~inst2->DstReg.WriteMask; - src_mask = get_src_arg_mask(mov, 0, dst_mask); - } - - /* Idem when the destination of mov is written */ - if (inst2->DstReg.File == mov->SrcReg[0].File && - (inst2->DstReg.RelAddr || - inst2->DstReg.Index == mov->SrcReg[0].Index)) { - src_mask &= ~inst2->DstReg.WriteMask; - dst_mask &= get_dst_mask_for_mov(mov, src_mask); - } - if (dst_mask == 0) - break; - } - } - - if (dbg) { - printf("Optimize: End remove extra move use.\n"); - /*_mesa_print_program(prog);*/ - } -} - - -/** - * Complements dead_code_global. Try to remove code in block of code by - * carefully monitoring the swizzles. Both functions should be merged into one - * with a proper control flow graph - */ -static GLboolean -_mesa_remove_dead_code_local(struct gl_program *prog) -{ - GLboolean *removeInst; - GLuint i, arg, rem = 0; - - removeInst = (GLboolean *) - calloc(1, prog->NumInstructions * sizeof(GLboolean)); - - for (i = 0; i < prog->NumInstructions; i++) { - const struct prog_instruction *inst = prog->Instructions + i; - const GLuint index = inst->DstReg.Index; - const GLuint mask = inst->DstReg.WriteMask; - enum inst_use use; - - /* We must deactivate the pass as soon as some indirection is used */ - if (inst->DstReg.RelAddr) - goto done; - for (arg = 0; arg < _mesa_num_inst_src_regs(inst->Opcode); arg++) - if (inst->SrcReg[arg].RelAddr) - goto done; - - if (_mesa_is_flow_control_opcode(inst->Opcode) || - _mesa_num_inst_dst_regs(inst->Opcode) == 0 || - inst->DstReg.File != PROGRAM_TEMPORARY || - inst->DstReg.RelAddr) - continue; - - use = find_next_use(prog, i+1, index, mask); - if (use == WRITE || use == END) - removeInst[i] = GL_TRUE; - } - - rem = remove_instructions(prog, removeInst); - -done: - free(removeInst); - return rem != 0; -} - - -/** - * Try to inject the destination of mov as the destination of inst and recompute - * the swizzles operators for the sources of inst if required. Return GL_TRUE - * of the substitution was possible, GL_FALSE otherwise - */ -static GLboolean -_mesa_merge_mov_into_inst(struct prog_instruction *inst, - const struct prog_instruction *mov) -{ - /* Indirection table which associates destination and source components for - * the mov instruction - */ - const GLuint mask = get_src_arg_mask(mov, 0, NO_MASK); - - /* Some components are not written by inst. We cannot remove the mov */ - if (mask != (inst->DstReg.WriteMask & mask)) - return GL_FALSE; - - /* Depending on the instruction, we may need to recompute the swizzles. - * Also, some other instructions (like TEX) are not linear. We will only - * consider completely active sources and destinations - */ - switch (inst->Opcode) { - - /* Carstesian instructions: we compute the swizzle */ - case OPCODE_MOV: - case OPCODE_MIN: - case OPCODE_MAX: - case OPCODE_ABS: - case OPCODE_ADD: - case OPCODE_MAD: - case OPCODE_MUL: - case OPCODE_SUB: - { - GLuint dst_to_src_comp[4] = {0,0,0,0}; - GLuint dst_comp, arg; - for (dst_comp = 0; dst_comp < 4; ++dst_comp) { - if (mov->DstReg.WriteMask & (1 << dst_comp)) { - const GLuint src_comp = GET_SWZ(mov->SrcReg[0].Swizzle, dst_comp); - ASSERT(src_comp < 4); - dst_to_src_comp[dst_comp] = src_comp; - } - } - - /* Patch each source of the instruction */ - for (arg = 0; arg < _mesa_num_inst_src_regs(inst->Opcode); arg++) { - const GLuint arg_swz = inst->SrcReg[arg].Swizzle; - inst->SrcReg[arg].Swizzle = 0; - - /* Reset each active component of the swizzle */ - for (dst_comp = 0; dst_comp < 4; ++dst_comp) { - GLuint src_comp, arg_comp; - if ((mov->DstReg.WriteMask & (1 << dst_comp)) == 0) - continue; - src_comp = dst_to_src_comp[dst_comp]; - ASSERT(src_comp < 4); - arg_comp = GET_SWZ(arg_swz, src_comp); - ASSERT(arg_comp < 4); - inst->SrcReg[arg].Swizzle |= arg_comp << (3*dst_comp); - } - } - inst->DstReg = mov->DstReg; - return GL_TRUE; - } - - /* Dot products and scalar instructions: we only change the destination */ - case OPCODE_RCP: - case OPCODE_SIN: - case OPCODE_COS: - case OPCODE_RSQ: - case OPCODE_POW: - case OPCODE_EX2: - case OPCODE_LOG: - case OPCODE_DP2: - case OPCODE_DP3: - case OPCODE_DP4: - inst->DstReg = mov->DstReg; - return GL_TRUE; - - /* All other instructions require fully active components with no swizzle */ - default: - if (mov->SrcReg[0].Swizzle != SWIZZLE_XYZW || - inst->DstReg.WriteMask != WRITEMASK_XYZW) - return GL_FALSE; - inst->DstReg = mov->DstReg; - return GL_TRUE; - } -} - - -/** - * Try to remove extraneous MOV instructions from the given program. - */ -static GLboolean -_mesa_remove_extra_moves(struct gl_program *prog) -{ - GLboolean *removeInst; /* per-instruction removal flag */ - GLuint i, rem = 0, nesting = 0; - - if (dbg) { - printf("Optimize: Begin remove extra moves\n"); - _mesa_print_program(prog); - } - - removeInst = (GLboolean *) - calloc(1, prog->NumInstructions * sizeof(GLboolean)); - - /* - * Look for sequences such as this: - * FOO tmpX, arg0, arg1; - * MOV tmpY, tmpX; - * and convert into: - * FOO tmpY, arg0, arg1; - */ - - for (i = 0; i < prog->NumInstructions; i++) { - const struct prog_instruction *mov = prog->Instructions + i; - - switch (mov->Opcode) { - case OPCODE_BGNLOOP: - case OPCODE_BGNSUB: - case OPCODE_IF: - nesting++; - break; - case OPCODE_ENDLOOP: - case OPCODE_ENDSUB: - case OPCODE_ENDIF: - nesting--; - break; - case OPCODE_MOV: - if (i > 0 && - can_downward_mov_be_modifed(mov) && - mov->SrcReg[0].File == PROGRAM_TEMPORARY && - nesting == 0) - { - - /* see if this MOV can be removed */ - const GLuint id = mov->SrcReg[0].Index; - struct prog_instruction *prevInst; - GLuint prevI; - - /* get pointer to previous instruction */ - prevI = i - 1; - while (prevI > 0 && removeInst[prevI]) - prevI--; - prevInst = prog->Instructions + prevI; - - if (prevInst->DstReg.File == PROGRAM_TEMPORARY && - prevInst->DstReg.Index == id && - prevInst->DstReg.RelAddr == 0 && - prevInst->DstReg.CondSrc == 0 && - prevInst->DstReg.CondMask == COND_TR) { - - const GLuint dst_mask = prevInst->DstReg.WriteMask; - enum inst_use next_use = find_next_use(prog, i+1, id, dst_mask); - - if (next_use == WRITE || next_use == END) { - /* OK, we can safely remove this MOV instruction. - * Transform: - * prevI: FOO tempIndex, x, y; - * i: MOV z, tempIndex; - * Into: - * prevI: FOO z, x, y; - */ - if (_mesa_merge_mov_into_inst(prevInst, mov)) { - removeInst[i] = GL_TRUE; - if (dbg) { - printf("Remove MOV at %u\n", i); - printf("new prev inst %u: ", prevI); - _mesa_print_instruction(prevInst); - } - } - } - } - } - break; - default: - ; /* nothing */ - } - } - - /* now remove the instructions which aren't needed */ - rem = remove_instructions(prog, removeInst); - - free(removeInst); - - if (dbg) { - printf("Optimize: End remove extra moves. %u instructions removed\n", rem); - /*_mesa_print_program(prog);*/ - } - - return rem != 0; -} - - -/** A live register interval */ -struct interval -{ - GLuint Reg; /** The temporary register index */ - GLuint Start, End; /** Start/end instruction numbers */ -}; - - -/** A list of register intervals */ -struct interval_list -{ - GLuint Num; - struct interval Intervals[REG_ALLOCATE_MAX_PROGRAM_TEMPS]; -}; - - -static void -append_interval(struct interval_list *list, const struct interval *inv) -{ - list->Intervals[list->Num++] = *inv; -} - - -/** Insert interval inv into list, sorted by interval end */ -static void -insert_interval_by_end(struct interval_list *list, const struct interval *inv) -{ - /* XXX we could do a binary search insertion here since list is sorted */ - GLint i = list->Num - 1; - while (i >= 0 && list->Intervals[i].End > inv->End) { - list->Intervals[i + 1] = list->Intervals[i]; - i--; - } - list->Intervals[i + 1] = *inv; - list->Num++; - -#ifdef DEBUG - { - GLuint i; - for (i = 0; i + 1 < list->Num; i++) { - ASSERT(list->Intervals[i].End <= list->Intervals[i + 1].End); - } - } -#endif -} - - -/** Remove the given interval from the interval list */ -static void -remove_interval(struct interval_list *list, const struct interval *inv) -{ - /* XXX we could binary search since list is sorted */ - GLuint k; - for (k = 0; k < list->Num; k++) { - if (list->Intervals[k].Reg == inv->Reg) { - /* found, remove it */ - ASSERT(list->Intervals[k].Start == inv->Start); - ASSERT(list->Intervals[k].End == inv->End); - while (k < list->Num - 1) { - list->Intervals[k] = list->Intervals[k + 1]; - k++; - } - list->Num--; - return; - } - } -} - - -/** called by qsort() */ -static int -compare_start(const void *a, const void *b) -{ - const struct interval *ia = (const struct interval *) a; - const struct interval *ib = (const struct interval *) b; - if (ia->Start < ib->Start) - return -1; - else if (ia->Start > ib->Start) - return +1; - else - return 0; -} - - -/** sort the interval list according to interval starts */ -static void -sort_interval_list_by_start(struct interval_list *list) -{ - qsort(list->Intervals, list->Num, sizeof(struct interval), compare_start); -#ifdef DEBUG - { - GLuint i; - for (i = 0; i + 1 < list->Num; i++) { - ASSERT(list->Intervals[i].Start <= list->Intervals[i + 1].Start); - } - } -#endif -} - -struct loop_info -{ - GLuint Start, End; /**< Start, end instructions of loop */ -}; - -/** - * Update the intermediate interval info for register 'index' and - * instruction 'ic'. - */ -static void -update_interval(GLint intBegin[], GLint intEnd[], - struct loop_info *loopStack, GLuint loopStackDepth, - GLuint index, GLuint ic) -{ - int i; - GLuint begin = ic; - GLuint end = ic; - - /* If the register is used in a loop, extend its lifetime through the end - * of the outermost loop that doesn't contain its definition. - */ - for (i = 0; i < loopStackDepth; i++) { - if (intBegin[index] < loopStack[i].Start) { - end = loopStack[i].End; - break; - } - } - - /* Variables that are live at the end of a loop will also be live at the - * beginning, so an instruction inside of a loop should have its live - * interval begin at the start of the outermost loop. - */ - if (loopStackDepth > 0 && ic > loopStack[0].Start && ic < loopStack[0].End) { - begin = loopStack[0].Start; - } - - ASSERT(index < REG_ALLOCATE_MAX_PROGRAM_TEMPS); - if (intBegin[index] == -1) { - ASSERT(intEnd[index] == -1); - intBegin[index] = begin; - intEnd[index] = end; - } - else { - intEnd[index] = end; - } -} - - -/** - * Find first/last instruction that references each temporary register. - */ -GLboolean -_mesa_find_temp_intervals(const struct prog_instruction *instructions, - GLuint numInstructions, - GLint intBegin[REG_ALLOCATE_MAX_PROGRAM_TEMPS], - GLint intEnd[REG_ALLOCATE_MAX_PROGRAM_TEMPS]) -{ - struct loop_info loopStack[MAX_LOOP_NESTING]; - GLuint loopStackDepth = 0; - GLuint i; - - for (i = 0; i < REG_ALLOCATE_MAX_PROGRAM_TEMPS; i++){ - intBegin[i] = intEnd[i] = -1; - } - - /* Scan instructions looking for temporary registers */ - for (i = 0; i < numInstructions; i++) { - const struct prog_instruction *inst = instructions + i; - if (inst->Opcode == OPCODE_BGNLOOP) { - loopStack[loopStackDepth].Start = i; - loopStack[loopStackDepth].End = inst->BranchTarget; - loopStackDepth++; - } - else if (inst->Opcode == OPCODE_ENDLOOP) { - loopStackDepth--; - } - else if (inst->Opcode == OPCODE_CAL) { - return GL_FALSE; - } - else { - const GLuint numSrc = 3;/*_mesa_num_inst_src_regs(inst->Opcode);*/ - GLuint j; - for (j = 0; j < numSrc; j++) { - if (inst->SrcReg[j].File == PROGRAM_TEMPORARY) { - const GLuint index = inst->SrcReg[j].Index; - if (inst->SrcReg[j].RelAddr) - return GL_FALSE; - update_interval(intBegin, intEnd, loopStack, loopStackDepth, - index, i); - } - } - if (inst->DstReg.File == PROGRAM_TEMPORARY) { - const GLuint index = inst->DstReg.Index; - if (inst->DstReg.RelAddr) - return GL_FALSE; - update_interval(intBegin, intEnd, loopStack, loopStackDepth, - index, i); - } - } - } - - return GL_TRUE; -} - - -/** - * Find the live intervals for each temporary register in the program. - * For register R, the interval [A,B] indicates that R is referenced - * from instruction A through instruction B. - * Special consideration is needed for loops and subroutines. - * \return GL_TRUE if success, GL_FALSE if we cannot proceed for some reason - */ -static GLboolean -find_live_intervals(struct gl_program *prog, - struct interval_list *liveIntervals) -{ - GLint intBegin[REG_ALLOCATE_MAX_PROGRAM_TEMPS]; - GLint intEnd[REG_ALLOCATE_MAX_PROGRAM_TEMPS]; - GLuint i; - - /* - * Note: we'll return GL_FALSE below if we find relative indexing - * into the TEMP register file. We can't handle that yet. - * We also give up on subroutines for now. - */ - - if (dbg) { - printf("Optimize: Begin find intervals\n"); - } - - /* build intermediate arrays */ - if (!_mesa_find_temp_intervals(prog->Instructions, prog->NumInstructions, - intBegin, intEnd)) - return GL_FALSE; - - /* Build live intervals list from intermediate arrays */ - liveIntervals->Num = 0; - for (i = 0; i < REG_ALLOCATE_MAX_PROGRAM_TEMPS; i++) { - if (intBegin[i] >= 0) { - struct interval inv; - inv.Reg = i; - inv.Start = intBegin[i]; - inv.End = intEnd[i]; - append_interval(liveIntervals, &inv); - } - } - - /* Sort the list according to interval starts */ - sort_interval_list_by_start(liveIntervals); - - if (dbg) { - /* print interval info */ - for (i = 0; i < liveIntervals->Num; i++) { - const struct interval *inv = liveIntervals->Intervals + i; - printf("Reg[%d] live [%d, %d]:", - inv->Reg, inv->Start, inv->End); - if (1) { - GLuint j; - for (j = 0; j < inv->Start; j++) - printf(" "); - for (j = inv->Start; j <= inv->End; j++) - printf("x"); - } - printf("\n"); - } - } - - return GL_TRUE; -} - - -/** Scan the array of used register flags to find free entry */ -static GLint -alloc_register(GLboolean usedRegs[REG_ALLOCATE_MAX_PROGRAM_TEMPS]) -{ - GLuint k; - for (k = 0; k < REG_ALLOCATE_MAX_PROGRAM_TEMPS; k++) { - if (!usedRegs[k]) { - usedRegs[k] = GL_TRUE; - return k; - } - } - return -1; -} - - -/** - * This function implements "Linear Scan Register Allocation" to reduce - * the number of temporary registers used by the program. - * - * We compute the "live interval" for all temporary registers then - * examine the overlap of the intervals to allocate new registers. - * Basically, if two intervals do not overlap, they can use the same register. - */ -static void -_mesa_reallocate_registers(struct gl_program *prog) -{ - struct interval_list liveIntervals; - GLint registerMap[REG_ALLOCATE_MAX_PROGRAM_TEMPS]; - GLboolean usedRegs[REG_ALLOCATE_MAX_PROGRAM_TEMPS]; - GLuint i; - GLint maxTemp = -1; - - if (dbg) { - printf("Optimize: Begin live-interval register reallocation\n"); - _mesa_print_program(prog); - } - - for (i = 0; i < REG_ALLOCATE_MAX_PROGRAM_TEMPS; i++){ - registerMap[i] = -1; - usedRegs[i] = GL_FALSE; - } - - if (!find_live_intervals(prog, &liveIntervals)) { - if (dbg) - printf("Aborting register reallocation\n"); - return; - } - - { - struct interval_list activeIntervals; - activeIntervals.Num = 0; - - /* loop over live intervals, allocating a new register for each */ - for (i = 0; i < liveIntervals.Num; i++) { - const struct interval *live = liveIntervals.Intervals + i; - - if (dbg) - printf("Consider register %u\n", live->Reg); - - /* Expire old intervals. Intervals which have ended with respect - * to the live interval can have their remapped registers freed. - */ - { - GLint j; - for (j = 0; j < (GLint) activeIntervals.Num; j++) { - const struct interval *inv = activeIntervals.Intervals + j; - if (inv->End >= live->Start) { - /* Stop now. Since the activeInterval list is sorted - * we know we don't have to go further. - */ - break; - } - else { - /* Interval 'inv' has expired */ - const GLint regNew = registerMap[inv->Reg]; - ASSERT(regNew >= 0); - - if (dbg) - printf(" expire interval for reg %u\n", inv->Reg); - - /* remove interval j from active list */ - remove_interval(&activeIntervals, inv); - j--; /* counter-act j++ in for-loop above */ - - /* return register regNew to the free pool */ - if (dbg) - printf(" free reg %d\n", regNew); - ASSERT(usedRegs[regNew] == GL_TRUE); - usedRegs[regNew] = GL_FALSE; - } - } - } - - /* find a free register for this live interval */ - { - const GLint k = alloc_register(usedRegs); - if (k < 0) { - /* out of registers, give up */ - return; - } - registerMap[live->Reg] = k; - maxTemp = MAX2(maxTemp, k); - if (dbg) - printf(" remap register %u -> %d\n", live->Reg, k); - } - - /* Insert this live interval into the active list which is sorted - * by increasing end points. - */ - insert_interval_by_end(&activeIntervals, live); - } - } - - if (maxTemp + 1 < (GLint) liveIntervals.Num) { - /* OK, we've reduced the number of registers needed. - * Scan the program and replace all the old temporary register - * indexes with the new indexes. - */ - replace_regs(prog, PROGRAM_TEMPORARY, registerMap); - - prog->NumTemporaries = maxTemp + 1; - } - - if (dbg) { - printf("Optimize: End live-interval register reallocation\n"); - printf("Num temp regs before: %u after: %u\n", - liveIntervals.Num, maxTemp + 1); - _mesa_print_program(prog); - } -} - - -#if 0 -static void -print_it(struct gl_context *ctx, struct gl_program *program, const char *txt) { - fprintf(stderr, "%s (%u inst):\n", txt, program->NumInstructions); - _mesa_print_program(program); - _mesa_print_program_parameters(ctx, program); - fprintf(stderr, "\n\n"); -} -#endif - -/** - * This pass replaces CMP T0, T1 T2 T0 with MOV T0, T2 when the CMP - * instruction is the first instruction to write to register T0. The are - * several lowering passes done in GLSL IR (e.g. branches and - * relative addressing) that create a large number of conditional assignments - * that ir_to_mesa converts to CMP instructions like the one mentioned above. - * - * Here is why this conversion is safe: - * CMP T0, T1 T2 T0 can be expanded to: - * if (T1 < 0.0) - * MOV T0, T2; - * else - * MOV T0, T0; - * - * If (T1 < 0.0) evaluates to true then our replacement MOV T0, T2 is the same - * as the original program. If (T1 < 0.0) evaluates to false, executing - * MOV T0, T0 will store a garbage value in T0 since T0 is uninitialized. - * Therefore, it doesn't matter that we are replacing MOV T0, T0 with MOV T0, T2 - * because any instruction that was going to read from T0 after this was going - * to read a garbage value anyway. - */ -static void -_mesa_simplify_cmp(struct gl_program * program) -{ - GLuint tempWrites[REG_ALLOCATE_MAX_PROGRAM_TEMPS]; - GLuint outputWrites[MAX_PROGRAM_OUTPUTS]; - GLuint i; - - if (dbg) { - printf("Optimize: Begin reads without writes\n"); - _mesa_print_program(program); - } - - for (i = 0; i < REG_ALLOCATE_MAX_PROGRAM_TEMPS; i++) { - tempWrites[i] = 0; - } - - for (i = 0; i < MAX_PROGRAM_OUTPUTS; i++) { - outputWrites[i] = 0; - } - - for (i = 0; i < program->NumInstructions; i++) { - struct prog_instruction *inst = program->Instructions + i; - GLuint prevWriteMask; - - /* Give up if we encounter relative addressing or flow control. */ - if (_mesa_is_flow_control_opcode(inst->Opcode) || inst->DstReg.RelAddr) { - return; - } - - if (inst->DstReg.File == PROGRAM_OUTPUT) { - assert(inst->DstReg.Index < MAX_PROGRAM_OUTPUTS); - prevWriteMask = outputWrites[inst->DstReg.Index]; - outputWrites[inst->DstReg.Index] |= inst->DstReg.WriteMask; - } else if (inst->DstReg.File == PROGRAM_TEMPORARY) { - assert(inst->DstReg.Index < REG_ALLOCATE_MAX_PROGRAM_TEMPS); - prevWriteMask = tempWrites[inst->DstReg.Index]; - tempWrites[inst->DstReg.Index] |= inst->DstReg.WriteMask; - } - - /* For a CMP to be considered a conditional write, the destination - * register and source register two must be the same. */ - if (inst->Opcode == OPCODE_CMP - && !(inst->DstReg.WriteMask & prevWriteMask) - && inst->SrcReg[2].File == inst->DstReg.File - && inst->SrcReg[2].Index == inst->DstReg.Index - && inst->DstReg.WriteMask == get_src_arg_mask(inst, 2, NO_MASK)) { - - inst->Opcode = OPCODE_MOV; - inst->SrcReg[0] = inst->SrcReg[1]; - } - } - if (dbg) { - printf("Optimize: End reads without writes\n"); - _mesa_print_program(program); - } -} - -/** - * Apply optimizations to the given program to eliminate unnecessary - * instructions, temp regs, etc. - */ -void -_mesa_optimize_program(struct gl_context *ctx, struct gl_program *program) -{ - GLboolean any_change; - - _mesa_simplify_cmp(program); - /* Stop when no modifications were output */ - do { - any_change = GL_FALSE; - _mesa_remove_extra_move_use(program); - if (_mesa_remove_dead_code_global(program)) - any_change = GL_TRUE; - if (_mesa_remove_extra_moves(program)) - any_change = GL_TRUE; - if (_mesa_remove_dead_code_local(program)) - any_change = GL_TRUE; - _mesa_reallocate_registers(program); - } while (any_change); -} - +/*
+ * Mesa 3-D graphics library
+ * Version: 7.5
+ *
+ * Copyright (C) 2009 VMware, Inc. 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
+ * VMWARE 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.
+ */
+
+
+
+#include "main/glheader.h"
+#include "main/context.h"
+#include "main/macros.h"
+#include "program.h"
+#include "prog_instruction.h"
+#include "prog_optimize.h"
+#include "prog_print.h"
+
+
+#define MAX_LOOP_NESTING 50
+/* MAX_PROGRAM_TEMPS is a low number (256), and we want to be able to
+ * register allocate many temporary values into that small number of
+ * temps. So allow large temporary indices coming into the register
+ * allocator.
+ */
+#define REG_ALLOCATE_MAX_PROGRAM_TEMPS ((1 << INST_INDEX_BITS) - 1)
+
+static GLboolean dbg = GL_FALSE;
+
+#define NO_MASK 0xf
+
+/**
+ * Returns the mask of channels (bitmask of WRITEMASK_X,Y,Z,W) which
+ * are read from the given src in this instruction, We also provide
+ * one optional masks which may mask other components in the dst
+ * register
+ */
+static GLuint
+get_src_arg_mask(const struct prog_instruction *inst,
+ GLuint arg, GLuint dst_mask)
+{
+ GLuint read_mask, channel_mask;
+ GLuint comp;
+
+ ASSERT(arg < _mesa_num_inst_src_regs(inst->Opcode));
+
+ /* Form the dst register, find the written channels */
+ if (inst->CondUpdate) {
+ channel_mask = WRITEMASK_XYZW;
+ }
+ else {
+ switch (inst->Opcode) {
+ case OPCODE_MOV:
+ case OPCODE_MIN:
+ case OPCODE_MAX:
+ case OPCODE_ABS:
+ case OPCODE_ADD:
+ case OPCODE_MAD:
+ case OPCODE_MUL:
+ case OPCODE_SUB:
+ case OPCODE_CMP:
+ case OPCODE_FLR:
+ case OPCODE_FRC:
+ case OPCODE_LRP:
+ case OPCODE_SEQ:
+ case OPCODE_SGE:
+ case OPCODE_SGT:
+ case OPCODE_SLE:
+ case OPCODE_SLT:
+ case OPCODE_SNE:
+ case OPCODE_SSG:
+ channel_mask = inst->DstReg.WriteMask & dst_mask;
+ break;
+ case OPCODE_RCP:
+ case OPCODE_SIN:
+ case OPCODE_COS:
+ case OPCODE_RSQ:
+ case OPCODE_POW:
+ case OPCODE_EX2:
+ case OPCODE_LOG:
+ channel_mask = WRITEMASK_X;
+ break;
+ case OPCODE_DP2:
+ channel_mask = WRITEMASK_XY;
+ break;
+ case OPCODE_DP3:
+ case OPCODE_XPD:
+ channel_mask = WRITEMASK_XYZ;
+ break;
+ default:
+ channel_mask = WRITEMASK_XYZW;
+ break;
+ }
+ }
+
+ /* Now, given the src swizzle and the written channels, find which
+ * components are actually read
+ */
+ read_mask = 0x0;
+ for (comp = 0; comp < 4; ++comp) {
+ const GLuint coord = GET_SWZ(inst->SrcReg[arg].Swizzle, comp);
+ ASSERT(coord < 4);
+ if (channel_mask & (1 << comp) && coord <= SWIZZLE_W)
+ read_mask |= 1 << coord;
+ }
+
+ return read_mask;
+}
+
+
+/**
+ * For a MOV instruction, compute a write mask when src register also has
+ * a mask
+ */
+static GLuint
+get_dst_mask_for_mov(const struct prog_instruction *mov, GLuint src_mask)
+{
+ const GLuint mask = mov->DstReg.WriteMask;
+ GLuint comp;
+ GLuint updated_mask = 0x0;
+
+ ASSERT(mov->Opcode == OPCODE_MOV);
+
+ for (comp = 0; comp < 4; ++comp) {
+ GLuint src_comp;
+ if ((mask & (1 << comp)) == 0)
+ continue;
+ src_comp = GET_SWZ(mov->SrcReg[0].Swizzle, comp);
+ if ((src_mask & (1 << src_comp)) == 0)
+ continue;
+ updated_mask |= 1 << comp;
+ }
+
+ return updated_mask;
+}
+
+
+/**
+ * Ensure that the swizzle is regular. That is, all of the swizzle
+ * terms are SWIZZLE_X,Y,Z,W and not SWIZZLE_ZERO or SWIZZLE_ONE.
+ */
+static GLboolean
+is_swizzle_regular(GLuint swz)
+{
+ return GET_SWZ(swz,0) <= SWIZZLE_W &&
+ GET_SWZ(swz,1) <= SWIZZLE_W &&
+ GET_SWZ(swz,2) <= SWIZZLE_W &&
+ GET_SWZ(swz,3) <= SWIZZLE_W;
+}
+
+
+/**
+ * In 'prog' remove instruction[i] if removeFlags[i] == TRUE.
+ * \return number of instructions removed
+ */
+static GLuint
+remove_instructions(struct gl_program *prog, const GLboolean *removeFlags)
+{
+ GLint i, removeEnd = 0, removeCount = 0;
+ GLuint totalRemoved = 0;
+
+ /* go backward */
+ for (i = prog->NumInstructions - 1; i >= 0; i--) {
+ if (removeFlags[i]) {
+ totalRemoved++;
+ if (removeCount == 0) {
+ /* begin a run of instructions to remove */
+ removeEnd = i;
+ removeCount = 1;
+ }
+ else {
+ /* extend the run of instructions to remove */
+ removeCount++;
+ }
+ }
+ else {
+ /* don't remove this instruction, but check if the preceeding
+ * instructions are to be removed.
+ */
+ if (removeCount > 0) {
+ GLint removeStart = removeEnd - removeCount + 1;
+ _mesa_delete_instructions(prog, removeStart, removeCount);
+ removeStart = removeCount = 0; /* reset removal info */
+ }
+ }
+ }
+ /* Finish removing if the first instruction was to be removed. */
+ if (removeCount > 0) {
+ GLint removeStart = removeEnd - removeCount + 1;
+ _mesa_delete_instructions(prog, removeStart, removeCount);
+ }
+ return totalRemoved;
+}
+
+
+/**
+ * Remap register indexes according to map.
+ * \param prog the program to search/replace
+ * \param file the type of register file to search/replace
+ * \param map maps old register indexes to new indexes
+ */
+static void
+replace_regs(struct gl_program *prog, gl_register_file file, const GLint map[])
+{
+ GLuint i;
+
+ 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 == file) {
+ GLuint index = inst->SrcReg[j].Index;
+ ASSERT(map[index] >= 0);
+ inst->SrcReg[j].Index = map[index];
+ }
+ }
+ if (inst->DstReg.File == file) {
+ const GLuint index = inst->DstReg.Index;
+ ASSERT(map[index] >= 0);
+ inst->DstReg.Index = map[index];
+ }
+ }
+}
+
+
+/**
+ * Remove dead instructions from the given program.
+ * This is very primitive for now. Basically look for temp registers
+ * that are written to but never read. Remove any instructions that
+ * write to such registers. Be careful with condition code setters.
+ */
+static GLboolean
+_mesa_remove_dead_code_global(struct gl_program *prog)
+{
+ GLboolean tempRead[REG_ALLOCATE_MAX_PROGRAM_TEMPS][4];
+ GLboolean *removeInst; /* per-instruction removal flag */
+ GLuint i, rem = 0, comp;
+
+ memset(tempRead, 0, sizeof(tempRead));
+
+ if (dbg) {
+ printf("Optimize: Begin dead code removal\n");
+ /*_mesa_print_program(prog);*/
+ }
+
+ removeInst = (GLboolean *)
+ calloc(1, prog->NumInstructions * sizeof(GLboolean));
+
+ /* Determine which temps are read and written */
+ for (i = 0; i < prog->NumInstructions; i++) {
+ const struct prog_instruction *inst = prog->Instructions + i;
+ const GLuint numSrc = _mesa_num_inst_src_regs(inst->Opcode);
+ GLuint j;
+
+ /* check src regs */
+ for (j = 0; j < numSrc; j++) {
+ if (inst->SrcReg[j].File == PROGRAM_TEMPORARY) {
+ const GLuint index = inst->SrcReg[j].Index;
+ GLuint read_mask;
+ ASSERT(index < REG_ALLOCATE_MAX_PROGRAM_TEMPS);
+ read_mask = get_src_arg_mask(inst, j, NO_MASK);
+
+ if (inst->SrcReg[j].RelAddr) {
+ if (dbg)
+ printf("abort remove dead code (indirect temp)\n");
+ goto done;
+ }
+
+ for (comp = 0; comp < 4; comp++) {
+ const GLuint swz = GET_SWZ(inst->SrcReg[j].Swizzle, comp);
+ ASSERT(swz < 4);
+ if ((read_mask & (1 << swz)) == 0)
+ continue;
+ if (swz <= SWIZZLE_W)
+ tempRead[index][swz] = GL_TRUE;
+ }
+ }
+ }
+
+ /* check dst reg */
+ if (inst->DstReg.File == PROGRAM_TEMPORARY) {
+ const GLuint index = inst->DstReg.Index;
+ ASSERT(index < REG_ALLOCATE_MAX_PROGRAM_TEMPS);
+
+ if (inst->DstReg.RelAddr) {
+ if (dbg)
+ printf("abort remove dead code (indirect temp)\n");
+ goto done;
+ }
+
+ if (inst->CondUpdate) {
+ /* If we're writing to this register and setting condition
+ * codes we cannot remove the instruction. Prevent removal
+ * by setting the 'read' flag.
+ */
+ tempRead[index][0] = GL_TRUE;
+ tempRead[index][1] = GL_TRUE;
+ tempRead[index][2] = GL_TRUE;
+ tempRead[index][3] = GL_TRUE;
+ }
+ }
+ }
+
+ /* find instructions that write to dead registers, flag for removal */
+ for (i = 0; i < prog->NumInstructions; i++) {
+ struct prog_instruction *inst = prog->Instructions + i;
+ const GLuint numDst = _mesa_num_inst_dst_regs(inst->Opcode);
+
+ if (numDst != 0 && inst->DstReg.File == PROGRAM_TEMPORARY) {
+ GLint chan, index = inst->DstReg.Index;
+
+ for (chan = 0; chan < 4; chan++) {
+ if (!tempRead[index][chan] &&
+ inst->DstReg.WriteMask & (1 << chan)) {
+ if (dbg) {
+ printf("Remove writemask on %u.%c\n", i,
+ chan == 3 ? 'w' : 'x' + chan);
+ }
+ inst->DstReg.WriteMask &= ~(1 << chan);
+ rem++;
+ }
+ }
+
+ if (inst->DstReg.WriteMask == 0) {
+ /* If we cleared all writes, the instruction can be removed. */
+ if (dbg)
+ printf("Remove instruction %u: \n", i);
+ removeInst[i] = GL_TRUE;
+ }
+ }
+ }
+
+ /* now remove the instructions which aren't needed */
+ rem = remove_instructions(prog, removeInst);
+
+ if (dbg) {
+ printf("Optimize: End dead code removal.\n");
+ printf(" %u channel writes removed\n", rem);
+ printf(" %u instructions removed\n", rem);
+ /*_mesa_print_program(prog);*/
+ }
+
+done:
+ free(removeInst);
+ return rem != 0;
+}
+
+
+enum inst_use
+{
+ READ,
+ WRITE,
+ FLOW,
+ END
+};
+
+
+/**
+ * Scan forward in program from 'start' for the next occurances of TEMP[index].
+ * We look if an instruction reads the component given by the masks and if they
+ * are overwritten.
+ * Return READ, WRITE, FLOW or END to indicate the next usage or an indicator
+ * that we can't look further.
+ */
+static enum inst_use
+find_next_use(const struct gl_program *prog,
+ GLuint start,
+ GLuint index,
+ GLuint mask)
+{
+ GLuint i;
+
+ for (i = start; i < prog->NumInstructions; i++) {
+ const struct prog_instruction *inst = prog->Instructions + i;
+ switch (inst->Opcode) {
+ case OPCODE_BGNLOOP:
+ case OPCODE_BGNSUB:
+ case OPCODE_BRA:
+ case OPCODE_CAL:
+ case OPCODE_CONT:
+ case OPCODE_IF:
+ case OPCODE_ELSE:
+ case OPCODE_ENDIF:
+ case OPCODE_ENDLOOP:
+ case OPCODE_ENDSUB:
+ case OPCODE_RET:
+ return FLOW;
+ case OPCODE_END:
+ return END;
+ default:
+ {
+ const GLuint numSrc = _mesa_num_inst_src_regs(inst->Opcode);
+ GLuint j;
+ for (j = 0; j < numSrc; j++) {
+ if (inst->SrcReg[j].RelAddr ||
+ (inst->SrcReg[j].File == PROGRAM_TEMPORARY &&
+ inst->SrcReg[j].Index == index &&
+ (get_src_arg_mask(inst,j,NO_MASK) & mask)))
+ return READ;
+ }
+ if (_mesa_num_inst_dst_regs(inst->Opcode) == 1 &&
+ inst->DstReg.File == PROGRAM_TEMPORARY &&
+ inst->DstReg.Index == index) {
+ mask &= ~inst->DstReg.WriteMask;
+ if (mask == 0)
+ return WRITE;
+ }
+ }
+ }
+ }
+ return END;
+}
+
+
+/**
+ * Is the given instruction opcode a flow-control opcode?
+ * XXX maybe move this into prog_instruction.[ch]
+ */
+static GLboolean
+_mesa_is_flow_control_opcode(enum prog_opcode opcode)
+{
+ switch (opcode) {
+ case OPCODE_BGNLOOP:
+ case OPCODE_BGNSUB:
+ case OPCODE_BRA:
+ case OPCODE_CAL:
+ case OPCODE_CONT:
+ case OPCODE_IF:
+ case OPCODE_ELSE:
+ case OPCODE_END:
+ case OPCODE_ENDIF:
+ case OPCODE_ENDLOOP:
+ case OPCODE_ENDSUB:
+ case OPCODE_RET:
+ return GL_TRUE;
+ default:
+ return GL_FALSE;
+ }
+}
+
+
+/**
+ * Test if the given instruction is a simple MOV (no conditional updating,
+ * not relative addressing, no negation/abs, etc).
+ */
+static GLboolean
+can_downward_mov_be_modifed(const struct prog_instruction *mov)
+{
+ return
+ mov->Opcode == OPCODE_MOV &&
+ mov->CondUpdate == GL_FALSE &&
+ mov->SrcReg[0].RelAddr == 0 &&
+ mov->SrcReg[0].Negate == 0 &&
+ mov->SrcReg[0].Abs == 0 &&
+ mov->SrcReg[0].HasIndex2 == 0 &&
+ mov->SrcReg[0].RelAddr2 == 0 &&
+ mov->DstReg.RelAddr == 0 &&
+ mov->DstReg.CondMask == COND_TR &&
+ mov->SaturateMode == SATURATE_OFF;
+}
+
+
+static GLboolean
+can_upward_mov_be_modifed(const struct prog_instruction *mov)
+{
+ return
+ can_downward_mov_be_modifed(mov) &&
+ mov->DstReg.File == PROGRAM_TEMPORARY;
+}
+
+
+/**
+ * Try to remove use of extraneous MOV instructions, to free them up for dead
+ * code removal.
+ */
+static void
+_mesa_remove_extra_move_use(struct gl_program *prog)
+{
+ GLuint i, j;
+
+ if (dbg) {
+ printf("Optimize: Begin remove extra move use\n");
+ _mesa_print_program(prog);
+ }
+
+ /*
+ * Look for sequences such as this:
+ * MOV tmpX, arg0;
+ * ...
+ * FOO tmpY, tmpX, arg1;
+ * and convert into:
+ * MOV tmpX, arg0;
+ * ...
+ * FOO tmpY, arg0, arg1;
+ */
+
+ for (i = 0; i + 1 < prog->NumInstructions; i++) {
+ const struct prog_instruction *mov = prog->Instructions + i;
+ GLuint dst_mask, src_mask;
+ if (can_upward_mov_be_modifed(mov) == GL_FALSE)
+ continue;
+
+ /* Scanning the code, we maintain the components which are still active in
+ * these two masks
+ */
+ dst_mask = mov->DstReg.WriteMask;
+ src_mask = get_src_arg_mask(mov, 0, NO_MASK);
+
+ /* Walk through remaining instructions until the or src reg gets
+ * rewritten or we get into some flow-control, eliminating the use of
+ * this MOV.
+ */
+ for (j = i + 1; j < prog->NumInstructions; j++) {
+ struct prog_instruction *inst2 = prog->Instructions + j;
+ GLuint arg;
+
+ if (_mesa_is_flow_control_opcode(inst2->Opcode))
+ break;
+
+ /* First rewrite this instruction's args if appropriate. */
+ for (arg = 0; arg < _mesa_num_inst_src_regs(inst2->Opcode); arg++) {
+ GLuint comp, read_mask;
+
+ if (inst2->SrcReg[arg].File != mov->DstReg.File ||
+ inst2->SrcReg[arg].Index != mov->DstReg.Index ||
+ inst2->SrcReg[arg].RelAddr ||
+ inst2->SrcReg[arg].Abs)
+ continue;
+ read_mask = get_src_arg_mask(inst2, arg, NO_MASK);
+
+ /* Adjust the swizzles of inst2 to point at MOV's source if ALL the
+ * components read still come from the mov instructions
+ */
+ if (is_swizzle_regular(inst2->SrcReg[arg].Swizzle) &&
+ (read_mask & dst_mask) == read_mask) {
+ for (comp = 0; comp < 4; comp++) {
+ const GLuint inst2_swz =
+ GET_SWZ(inst2->SrcReg[arg].Swizzle, comp);
+ const GLuint s = GET_SWZ(mov->SrcReg[0].Swizzle, inst2_swz);
+ inst2->SrcReg[arg].Swizzle &= ~(7 << (3 * comp));
+ inst2->SrcReg[arg].Swizzle |= s << (3 * comp);
+ inst2->SrcReg[arg].Negate ^= (((mov->SrcReg[0].Negate >>
+ inst2_swz) & 0x1) << comp);
+ }
+ inst2->SrcReg[arg].File = mov->SrcReg[0].File;
+ inst2->SrcReg[arg].Index = mov->SrcReg[0].Index;
+ }
+ }
+
+ /* The source of MOV is written. This potentially deactivates some
+ * components from the src and dst of the MOV instruction
+ */
+ if (inst2->DstReg.File == mov->DstReg.File &&
+ (inst2->DstReg.RelAddr ||
+ inst2->DstReg.Index == mov->DstReg.Index)) {
+ dst_mask &= ~inst2->DstReg.WriteMask;
+ src_mask = get_src_arg_mask(mov, 0, dst_mask);
+ }
+
+ /* Idem when the destination of mov is written */
+ if (inst2->DstReg.File == mov->SrcReg[0].File &&
+ (inst2->DstReg.RelAddr ||
+ inst2->DstReg.Index == mov->SrcReg[0].Index)) {
+ src_mask &= ~inst2->DstReg.WriteMask;
+ dst_mask &= get_dst_mask_for_mov(mov, src_mask);
+ }
+ if (dst_mask == 0)
+ break;
+ }
+ }
+
+ if (dbg) {
+ printf("Optimize: End remove extra move use.\n");
+ /*_mesa_print_program(prog);*/
+ }
+}
+
+
+/**
+ * Complements dead_code_global. Try to remove code in block of code by
+ * carefully monitoring the swizzles. Both functions should be merged into one
+ * with a proper control flow graph
+ */
+static GLboolean
+_mesa_remove_dead_code_local(struct gl_program *prog)
+{
+ GLboolean *removeInst;
+ GLuint i, arg, rem = 0;
+
+ removeInst = (GLboolean *)
+ calloc(1, prog->NumInstructions * sizeof(GLboolean));
+
+ for (i = 0; i < prog->NumInstructions; i++) {
+ const struct prog_instruction *inst = prog->Instructions + i;
+ const GLuint index = inst->DstReg.Index;
+ const GLuint mask = inst->DstReg.WriteMask;
+ enum inst_use use;
+
+ /* We must deactivate the pass as soon as some indirection is used */
+ if (inst->DstReg.RelAddr)
+ goto done;
+ for (arg = 0; arg < _mesa_num_inst_src_regs(inst->Opcode); arg++)
+ if (inst->SrcReg[arg].RelAddr)
+ goto done;
+
+ if (_mesa_is_flow_control_opcode(inst->Opcode) ||
+ _mesa_num_inst_dst_regs(inst->Opcode) == 0 ||
+ inst->DstReg.File != PROGRAM_TEMPORARY ||
+ inst->DstReg.RelAddr)
+ continue;
+
+ use = find_next_use(prog, i+1, index, mask);
+ if (use == WRITE || use == END)
+ removeInst[i] = GL_TRUE;
+ }
+
+ rem = remove_instructions(prog, removeInst);
+
+done:
+ free(removeInst);
+ return rem != 0;
+}
+
+
+/**
+ * Try to inject the destination of mov as the destination of inst and recompute
+ * the swizzles operators for the sources of inst if required. Return GL_TRUE
+ * of the substitution was possible, GL_FALSE otherwise
+ */
+static GLboolean
+_mesa_merge_mov_into_inst(struct prog_instruction *inst,
+ const struct prog_instruction *mov)
+{
+ /* Indirection table which associates destination and source components for
+ * the mov instruction
+ */
+ const GLuint mask = get_src_arg_mask(mov, 0, NO_MASK);
+
+ /* Some components are not written by inst. We cannot remove the mov */
+ if (mask != (inst->DstReg.WriteMask & mask))
+ return GL_FALSE;
+
+ /* Depending on the instruction, we may need to recompute the swizzles.
+ * Also, some other instructions (like TEX) are not linear. We will only
+ * consider completely active sources and destinations
+ */
+ switch (inst->Opcode) {
+
+ /* Carstesian instructions: we compute the swizzle */
+ case OPCODE_MOV:
+ case OPCODE_MIN:
+ case OPCODE_MAX:
+ case OPCODE_ABS:
+ case OPCODE_ADD:
+ case OPCODE_MAD:
+ case OPCODE_MUL:
+ case OPCODE_SUB:
+ {
+ GLuint dst_to_src_comp[4] = {0,0,0,0};
+ GLuint dst_comp, arg;
+ for (dst_comp = 0; dst_comp < 4; ++dst_comp) {
+ if (mov->DstReg.WriteMask & (1 << dst_comp)) {
+ const GLuint src_comp = GET_SWZ(mov->SrcReg[0].Swizzle, dst_comp);
+ ASSERT(src_comp < 4);
+ dst_to_src_comp[dst_comp] = src_comp;
+ }
+ }
+
+ /* Patch each source of the instruction */
+ for (arg = 0; arg < _mesa_num_inst_src_regs(inst->Opcode); arg++) {
+ const GLuint arg_swz = inst->SrcReg[arg].Swizzle;
+ inst->SrcReg[arg].Swizzle = 0;
+
+ /* Reset each active component of the swizzle */
+ for (dst_comp = 0; dst_comp < 4; ++dst_comp) {
+ GLuint src_comp, arg_comp;
+ if ((mov->DstReg.WriteMask & (1 << dst_comp)) == 0)
+ continue;
+ src_comp = dst_to_src_comp[dst_comp];
+ ASSERT(src_comp < 4);
+ arg_comp = GET_SWZ(arg_swz, src_comp);
+ ASSERT(arg_comp < 4);
+ inst->SrcReg[arg].Swizzle |= arg_comp << (3*dst_comp);
+ }
+ }
+ inst->DstReg = mov->DstReg;
+ return GL_TRUE;
+ }
+
+ /* Dot products and scalar instructions: we only change the destination */
+ case OPCODE_RCP:
+ case OPCODE_SIN:
+ case OPCODE_COS:
+ case OPCODE_RSQ:
+ case OPCODE_POW:
+ case OPCODE_EX2:
+ case OPCODE_LOG:
+ case OPCODE_DP2:
+ case OPCODE_DP3:
+ case OPCODE_DP4:
+ inst->DstReg = mov->DstReg;
+ return GL_TRUE;
+
+ /* All other instructions require fully active components with no swizzle */
+ default:
+ if (mov->SrcReg[0].Swizzle != SWIZZLE_XYZW ||
+ inst->DstReg.WriteMask != WRITEMASK_XYZW)
+ return GL_FALSE;
+ inst->DstReg = mov->DstReg;
+ return GL_TRUE;
+ }
+}
+
+
+/**
+ * Try to remove extraneous MOV instructions from the given program.
+ */
+static GLboolean
+_mesa_remove_extra_moves(struct gl_program *prog)
+{
+ GLboolean *removeInst; /* per-instruction removal flag */
+ GLuint i, rem = 0, nesting = 0;
+
+ if (dbg) {
+ printf("Optimize: Begin remove extra moves\n");
+ _mesa_print_program(prog);
+ }
+
+ removeInst = (GLboolean *)
+ calloc(1, prog->NumInstructions * sizeof(GLboolean));
+
+ /*
+ * Look for sequences such as this:
+ * FOO tmpX, arg0, arg1;
+ * MOV tmpY, tmpX;
+ * and convert into:
+ * FOO tmpY, arg0, arg1;
+ */
+
+ for (i = 0; i < prog->NumInstructions; i++) {
+ const struct prog_instruction *mov = prog->Instructions + i;
+
+ switch (mov->Opcode) {
+ case OPCODE_BGNLOOP:
+ case OPCODE_BGNSUB:
+ case OPCODE_IF:
+ nesting++;
+ break;
+ case OPCODE_ENDLOOP:
+ case OPCODE_ENDSUB:
+ case OPCODE_ENDIF:
+ nesting--;
+ break;
+ case OPCODE_MOV:
+ if (i > 0 &&
+ can_downward_mov_be_modifed(mov) &&
+ mov->SrcReg[0].File == PROGRAM_TEMPORARY &&
+ nesting == 0)
+ {
+
+ /* see if this MOV can be removed */
+ const GLuint id = mov->SrcReg[0].Index;
+ struct prog_instruction *prevInst;
+ GLuint prevI;
+
+ /* get pointer to previous instruction */
+ prevI = i - 1;
+ while (prevI > 0 && removeInst[prevI])
+ prevI--;
+ prevInst = prog->Instructions + prevI;
+
+ if (prevInst->DstReg.File == PROGRAM_TEMPORARY &&
+ prevInst->DstReg.Index == id &&
+ prevInst->DstReg.RelAddr == 0 &&
+ prevInst->DstReg.CondSrc == 0 &&
+ prevInst->DstReg.CondMask == COND_TR) {
+
+ const GLuint dst_mask = prevInst->DstReg.WriteMask;
+ enum inst_use next_use = find_next_use(prog, i+1, id, dst_mask);
+
+ if (next_use == WRITE || next_use == END) {
+ /* OK, we can safely remove this MOV instruction.
+ * Transform:
+ * prevI: FOO tempIndex, x, y;
+ * i: MOV z, tempIndex;
+ * Into:
+ * prevI: FOO z, x, y;
+ */
+ if (_mesa_merge_mov_into_inst(prevInst, mov)) {
+ removeInst[i] = GL_TRUE;
+ if (dbg) {
+ printf("Remove MOV at %u\n", i);
+ printf("new prev inst %u: ", prevI);
+ _mesa_print_instruction(prevInst);
+ }
+ }
+ }
+ }
+ }
+ break;
+ default:
+ ; /* nothing */
+ }
+ }
+
+ /* now remove the instructions which aren't needed */
+ rem = remove_instructions(prog, removeInst);
+
+ free(removeInst);
+
+ if (dbg) {
+ printf("Optimize: End remove extra moves. %u instructions removed\n", rem);
+ /*_mesa_print_program(prog);*/
+ }
+
+ return rem != 0;
+}
+
+
+/** A live register interval */
+struct interval
+{
+ GLuint Reg; /** The temporary register index */
+ GLuint Start, End; /** Start/end instruction numbers */
+};
+
+
+/** A list of register intervals */
+struct interval_list
+{
+ GLuint Num;
+ struct interval Intervals[REG_ALLOCATE_MAX_PROGRAM_TEMPS];
+};
+
+
+static void
+append_interval(struct interval_list *list, const struct interval *inv)
+{
+ list->Intervals[list->Num++] = *inv;
+}
+
+
+/** Insert interval inv into list, sorted by interval end */
+static void
+insert_interval_by_end(struct interval_list *list, const struct interval *inv)
+{
+ /* XXX we could do a binary search insertion here since list is sorted */
+ GLint i = list->Num - 1;
+ while (i >= 0 && list->Intervals[i].End > inv->End) {
+ list->Intervals[i + 1] = list->Intervals[i];
+ i--;
+ }
+ list->Intervals[i + 1] = *inv;
+ list->Num++;
+
+#ifdef DEBUG
+ {
+ GLuint i;
+ for (i = 0; i + 1 < list->Num; i++) {
+ ASSERT(list->Intervals[i].End <= list->Intervals[i + 1].End);
+ }
+ }
+#endif
+}
+
+
+/** Remove the given interval from the interval list */
+static void
+remove_interval(struct interval_list *list, const struct interval *inv)
+{
+ /* XXX we could binary search since list is sorted */
+ GLuint k;
+ for (k = 0; k < list->Num; k++) {
+ if (list->Intervals[k].Reg == inv->Reg) {
+ /* found, remove it */
+ ASSERT(list->Intervals[k].Start == inv->Start);
+ ASSERT(list->Intervals[k].End == inv->End);
+ while (k < list->Num - 1) {
+ list->Intervals[k] = list->Intervals[k + 1];
+ k++;
+ }
+ list->Num--;
+ return;
+ }
+ }
+}
+
+
+/** called by qsort() */
+static int
+compare_start(const void *a, const void *b)
+{
+ const struct interval *ia = (const struct interval *) a;
+ const struct interval *ib = (const struct interval *) b;
+ if (ia->Start < ib->Start)
+ return -1;
+ else if (ia->Start > ib->Start)
+ return +1;
+ else
+ return 0;
+}
+
+
+/** sort the interval list according to interval starts */
+static void
+sort_interval_list_by_start(struct interval_list *list)
+{
+ qsort(list->Intervals, list->Num, sizeof(struct interval), compare_start);
+#ifdef DEBUG
+ {
+ GLuint i;
+ for (i = 0; i + 1 < list->Num; i++) {
+ ASSERT(list->Intervals[i].Start <= list->Intervals[i + 1].Start);
+ }
+ }
+#endif
+}
+
+struct loop_info
+{
+ GLuint Start, End; /**< Start, end instructions of loop */
+};
+
+/**
+ * Update the intermediate interval info for register 'index' and
+ * instruction 'ic'.
+ */
+static void
+update_interval(GLint intBegin[], GLint intEnd[],
+ struct loop_info *loopStack, GLuint loopStackDepth,
+ GLuint index, GLuint ic)
+{
+ int i;
+ GLuint begin = ic;
+ GLuint end = ic;
+
+ /* If the register is used in a loop, extend its lifetime through the end
+ * of the outermost loop that doesn't contain its definition.
+ */
+ for (i = 0; i < loopStackDepth; i++) {
+ if (intBegin[index] < loopStack[i].Start) {
+ end = loopStack[i].End;
+ break;
+ }
+ }
+
+ /* Variables that are live at the end of a loop will also be live at the
+ * beginning, so an instruction inside of a loop should have its live
+ * interval begin at the start of the outermost loop.
+ */
+ if (loopStackDepth > 0 && ic > loopStack[0].Start && ic < loopStack[0].End) {
+ begin = loopStack[0].Start;
+ }
+
+ ASSERT(index < REG_ALLOCATE_MAX_PROGRAM_TEMPS);
+ if (intBegin[index] == -1) {
+ ASSERT(intEnd[index] == -1);
+ intBegin[index] = begin;
+ intEnd[index] = end;
+ }
+ else {
+ intEnd[index] = end;
+ }
+}
+
+
+/**
+ * Find first/last instruction that references each temporary register.
+ */
+GLboolean
+_mesa_find_temp_intervals(const struct prog_instruction *instructions,
+ GLuint numInstructions,
+ GLint intBegin[REG_ALLOCATE_MAX_PROGRAM_TEMPS],
+ GLint intEnd[REG_ALLOCATE_MAX_PROGRAM_TEMPS])
+{
+ struct loop_info loopStack[MAX_LOOP_NESTING];
+ GLuint loopStackDepth = 0;
+ GLuint i;
+
+ for (i = 0; i < REG_ALLOCATE_MAX_PROGRAM_TEMPS; i++){
+ intBegin[i] = intEnd[i] = -1;
+ }
+
+ /* Scan instructions looking for temporary registers */
+ for (i = 0; i < numInstructions; i++) {
+ const struct prog_instruction *inst = instructions + i;
+ if (inst->Opcode == OPCODE_BGNLOOP) {
+ loopStack[loopStackDepth].Start = i;
+ loopStack[loopStackDepth].End = inst->BranchTarget;
+ loopStackDepth++;
+ }
+ else if (inst->Opcode == OPCODE_ENDLOOP) {
+ loopStackDepth--;
+ }
+ else if (inst->Opcode == OPCODE_CAL) {
+ return GL_FALSE;
+ }
+ else {
+ const GLuint numSrc = 3;/*_mesa_num_inst_src_regs(inst->Opcode);*/
+ GLuint j;
+ for (j = 0; j < numSrc; j++) {
+ if (inst->SrcReg[j].File == PROGRAM_TEMPORARY) {
+ const GLuint index = inst->SrcReg[j].Index;
+ if (inst->SrcReg[j].RelAddr)
+ return GL_FALSE;
+ update_interval(intBegin, intEnd, loopStack, loopStackDepth,
+ index, i);
+ }
+ }
+ if (inst->DstReg.File == PROGRAM_TEMPORARY) {
+ const GLuint index = inst->DstReg.Index;
+ if (inst->DstReg.RelAddr)
+ return GL_FALSE;
+ update_interval(intBegin, intEnd, loopStack, loopStackDepth,
+ index, i);
+ }
+ }
+ }
+
+ return GL_TRUE;
+}
+
+
+/**
+ * Find the live intervals for each temporary register in the program.
+ * For register R, the interval [A,B] indicates that R is referenced
+ * from instruction A through instruction B.
+ * Special consideration is needed for loops and subroutines.
+ * \return GL_TRUE if success, GL_FALSE if we cannot proceed for some reason
+ */
+static GLboolean
+find_live_intervals(struct gl_program *prog,
+ struct interval_list *liveIntervals)
+{
+ GLint intBegin[REG_ALLOCATE_MAX_PROGRAM_TEMPS];
+ GLint intEnd[REG_ALLOCATE_MAX_PROGRAM_TEMPS];
+ GLuint i;
+
+ /*
+ * Note: we'll return GL_FALSE below if we find relative indexing
+ * into the TEMP register file. We can't handle that yet.
+ * We also give up on subroutines for now.
+ */
+
+ if (dbg) {
+ printf("Optimize: Begin find intervals\n");
+ }
+
+ /* build intermediate arrays */
+ if (!_mesa_find_temp_intervals(prog->Instructions, prog->NumInstructions,
+ intBegin, intEnd))
+ return GL_FALSE;
+
+ /* Build live intervals list from intermediate arrays */
+ liveIntervals->Num = 0;
+ for (i = 0; i < REG_ALLOCATE_MAX_PROGRAM_TEMPS; i++) {
+ if (intBegin[i] >= 0) {
+ struct interval inv;
+ inv.Reg = i;
+ inv.Start = intBegin[i];
+ inv.End = intEnd[i];
+ append_interval(liveIntervals, &inv);
+ }
+ }
+
+ /* Sort the list according to interval starts */
+ sort_interval_list_by_start(liveIntervals);
+
+ if (dbg) {
+ /* print interval info */
+ for (i = 0; i < liveIntervals->Num; i++) {
+ const struct interval *inv = liveIntervals->Intervals + i;
+ printf("Reg[%d] live [%d, %d]:",
+ inv->Reg, inv->Start, inv->End);
+ if (1) {
+ GLuint j;
+ for (j = 0; j < inv->Start; j++)
+ printf(" ");
+ for (j = inv->Start; j <= inv->End; j++)
+ printf("x");
+ }
+ printf("\n");
+ }
+ }
+
+ return GL_TRUE;
+}
+
+
+/** Scan the array of used register flags to find free entry */
+static GLint
+alloc_register(GLboolean usedRegs[REG_ALLOCATE_MAX_PROGRAM_TEMPS])
+{
+ GLuint k;
+ for (k = 0; k < REG_ALLOCATE_MAX_PROGRAM_TEMPS; k++) {
+ if (!usedRegs[k]) {
+ usedRegs[k] = GL_TRUE;
+ return k;
+ }
+ }
+ return -1;
+}
+
+
+/**
+ * This function implements "Linear Scan Register Allocation" to reduce
+ * the number of temporary registers used by the program.
+ *
+ * We compute the "live interval" for all temporary registers then
+ * examine the overlap of the intervals to allocate new registers.
+ * Basically, if two intervals do not overlap, they can use the same register.
+ */
+static void
+_mesa_reallocate_registers(struct gl_program *prog)
+{
+ struct interval_list liveIntervals;
+ GLint registerMap[REG_ALLOCATE_MAX_PROGRAM_TEMPS];
+ GLboolean usedRegs[REG_ALLOCATE_MAX_PROGRAM_TEMPS];
+ GLuint i;
+ GLint maxTemp = -1;
+
+ if (dbg) {
+ printf("Optimize: Begin live-interval register reallocation\n");
+ _mesa_print_program(prog);
+ }
+
+ for (i = 0; i < REG_ALLOCATE_MAX_PROGRAM_TEMPS; i++){
+ registerMap[i] = -1;
+ usedRegs[i] = GL_FALSE;
+ }
+
+ if (!find_live_intervals(prog, &liveIntervals)) {
+ if (dbg)
+ printf("Aborting register reallocation\n");
+ return;
+ }
+
+ {
+ struct interval_list activeIntervals;
+ activeIntervals.Num = 0;
+
+ /* loop over live intervals, allocating a new register for each */
+ for (i = 0; i < liveIntervals.Num; i++) {
+ const struct interval *live = liveIntervals.Intervals + i;
+
+ if (dbg)
+ printf("Consider register %u\n", live->Reg);
+
+ /* Expire old intervals. Intervals which have ended with respect
+ * to the live interval can have their remapped registers freed.
+ */
+ {
+ GLint j;
+ for (j = 0; j < (GLint) activeIntervals.Num; j++) {
+ const struct interval *inv = activeIntervals.Intervals + j;
+ if (inv->End >= live->Start) {
+ /* Stop now. Since the activeInterval list is sorted
+ * we know we don't have to go further.
+ */
+ break;
+ }
+ else {
+ /* Interval 'inv' has expired */
+ const GLint regNew = registerMap[inv->Reg];
+ ASSERT(regNew >= 0);
+
+ if (dbg)
+ printf(" expire interval for reg %u\n", inv->Reg);
+
+ /* remove interval j from active list */
+ remove_interval(&activeIntervals, inv);
+ j--; /* counter-act j++ in for-loop above */
+
+ /* return register regNew to the free pool */
+ if (dbg)
+ printf(" free reg %d\n", regNew);
+ ASSERT(usedRegs[regNew] == GL_TRUE);
+ usedRegs[regNew] = GL_FALSE;
+ }
+ }
+ }
+
+ /* find a free register for this live interval */
+ {
+ const GLint k = alloc_register(usedRegs);
+ if (k < 0) {
+ /* out of registers, give up */
+ return;
+ }
+ registerMap[live->Reg] = k;
+ maxTemp = MAX2(maxTemp, k);
+ if (dbg)
+ printf(" remap register %u -> %d\n", live->Reg, k);
+ }
+
+ /* Insert this live interval into the active list which is sorted
+ * by increasing end points.
+ */
+ insert_interval_by_end(&activeIntervals, live);
+ }
+ }
+
+ if (maxTemp + 1 < (GLint) liveIntervals.Num) {
+ /* OK, we've reduced the number of registers needed.
+ * Scan the program and replace all the old temporary register
+ * indexes with the new indexes.
+ */
+ replace_regs(prog, PROGRAM_TEMPORARY, registerMap);
+
+ prog->NumTemporaries = maxTemp + 1;
+ }
+
+ if (dbg) {
+ printf("Optimize: End live-interval register reallocation\n");
+ printf("Num temp regs before: %u after: %u\n",
+ liveIntervals.Num, maxTemp + 1);
+ _mesa_print_program(prog);
+ }
+}
+
+
+#if 0
+static void
+print_it(struct gl_context *ctx, struct gl_program *program, const char *txt) {
+ fprintf(stderr, "%s (%u inst):\n", txt, program->NumInstructions);
+ _mesa_print_program(program);
+ _mesa_print_program_parameters(ctx, program);
+ fprintf(stderr, "\n\n");
+}
+#endif
+
+/**
+ * This pass replaces CMP T0, T1 T2 T0 with MOV T0, T2 when the CMP
+ * instruction is the first instruction to write to register T0. The are
+ * several lowering passes done in GLSL IR (e.g. branches and
+ * relative addressing) that create a large number of conditional assignments
+ * that ir_to_mesa converts to CMP instructions like the one mentioned above.
+ *
+ * Here is why this conversion is safe:
+ * CMP T0, T1 T2 T0 can be expanded to:
+ * if (T1 < 0.0)
+ * MOV T0, T2;
+ * else
+ * MOV T0, T0;
+ *
+ * If (T1 < 0.0) evaluates to true then our replacement MOV T0, T2 is the same
+ * as the original program. If (T1 < 0.0) evaluates to false, executing
+ * MOV T0, T0 will store a garbage value in T0 since T0 is uninitialized.
+ * Therefore, it doesn't matter that we are replacing MOV T0, T0 with MOV T0, T2
+ * because any instruction that was going to read from T0 after this was going
+ * to read a garbage value anyway.
+ */
+static void
+_mesa_simplify_cmp(struct gl_program * program)
+{
+ GLuint tempWrites[REG_ALLOCATE_MAX_PROGRAM_TEMPS];
+ GLuint outputWrites[MAX_PROGRAM_OUTPUTS];
+ GLuint i;
+
+ if (dbg) {
+ printf("Optimize: Begin reads without writes\n");
+ _mesa_print_program(program);
+ }
+
+ for (i = 0; i < REG_ALLOCATE_MAX_PROGRAM_TEMPS; i++) {
+ tempWrites[i] = 0;
+ }
+
+ for (i = 0; i < MAX_PROGRAM_OUTPUTS; i++) {
+ outputWrites[i] = 0;
+ }
+
+ for (i = 0; i < program->NumInstructions; i++) {
+ struct prog_instruction *inst = program->Instructions + i;
+ GLuint prevWriteMask;
+
+ /* Give up if we encounter relative addressing or flow control. */
+ if (_mesa_is_flow_control_opcode(inst->Opcode) || inst->DstReg.RelAddr) {
+ return;
+ }
+
+ if (inst->DstReg.File == PROGRAM_OUTPUT) {
+ assert(inst->DstReg.Index < MAX_PROGRAM_OUTPUTS);
+ prevWriteMask = outputWrites[inst->DstReg.Index];
+ outputWrites[inst->DstReg.Index] |= inst->DstReg.WriteMask;
+ } else if (inst->DstReg.File == PROGRAM_TEMPORARY) {
+ assert(inst->DstReg.Index < REG_ALLOCATE_MAX_PROGRAM_TEMPS);
+ prevWriteMask = tempWrites[inst->DstReg.Index];
+ tempWrites[inst->DstReg.Index] |= inst->DstReg.WriteMask;
+ }
+
+ /* For a CMP to be considered a conditional write, the destination
+ * register and source register two must be the same. */
+ if (inst->Opcode == OPCODE_CMP
+ && !(inst->DstReg.WriteMask & prevWriteMask)
+ && inst->SrcReg[2].File == inst->DstReg.File
+ && inst->SrcReg[2].Index == inst->DstReg.Index
+ && inst->DstReg.WriteMask == get_src_arg_mask(inst, 2, NO_MASK)) {
+
+ inst->Opcode = OPCODE_MOV;
+ inst->SrcReg[0] = inst->SrcReg[1];
+ }
+ }
+ if (dbg) {
+ printf("Optimize: End reads without writes\n");
+ _mesa_print_program(program);
+ }
+}
+
+/**
+ * Apply optimizations to the given program to eliminate unnecessary
+ * instructions, temp regs, etc.
+ */
+void
+_mesa_optimize_program(struct gl_context *ctx, struct gl_program *program)
+{
+ GLboolean any_change;
+
+ _mesa_simplify_cmp(program);
+ /* Stop when no modifications were output */
+ do {
+ any_change = GL_FALSE;
+ _mesa_remove_extra_move_use(program);
+ if (_mesa_remove_dead_code_global(program))
+ any_change = GL_TRUE;
+ if (_mesa_remove_extra_moves(program))
+ any_change = GL_TRUE;
+ if (_mesa_remove_dead_code_local(program))
+ any_change = GL_TRUE;
+ _mesa_reallocate_registers(program);
+ } while (any_change);
+}
+
diff --git a/mesalib/src/mesa/program/prog_print.c b/mesalib/src/mesa/program/prog_print.c index 7c3b4909e..248f4d730 100644 --- a/mesalib/src/mesa/program/prog_print.c +++ b/mesalib/src/mesa/program/prog_print.c @@ -1,1096 +1,1096 @@ -/* - * Mesa 3-D graphics library - * Version: 7.3 - * - * Copyright (C) 1999-2008 Brian Paul All Rights Reserved. - * Copyright (C) 2009 VMware, Inc. 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_print.c - * Print vertex/fragment programs - for debugging. - * \author Brian Paul - */ - -#include "main/glheader.h" -#include "main/context.h" -#include "main/imports.h" -#include "prog_instruction.h" -#include "prog_parameter.h" -#include "prog_print.h" -#include "prog_statevars.h" - - - -/** - * Return string name for given program/register file. - */ -const char * -_mesa_register_file_name(gl_register_file f) -{ - switch (f) { - case PROGRAM_TEMPORARY: - return "TEMP"; - case PROGRAM_LOCAL_PARAM: - return "LOCAL"; - case PROGRAM_ENV_PARAM: - return "ENV"; - case PROGRAM_STATE_VAR: - return "STATE"; - case PROGRAM_INPUT: - return "INPUT"; - case PROGRAM_OUTPUT: - return "OUTPUT"; - case PROGRAM_NAMED_PARAM: - return "NAMED"; - case PROGRAM_CONSTANT: - return "CONST"; - case PROGRAM_UNIFORM: - return "UNIFORM"; - case PROGRAM_VARYING: - return "VARYING"; - case PROGRAM_WRITE_ONLY: - return "WRITE_ONLY"; - case PROGRAM_ADDRESS: - return "ADDR"; - case PROGRAM_SAMPLER: - return "SAMPLER"; - case PROGRAM_SYSTEM_VALUE: - return "SYSVAL"; - case PROGRAM_UNDEFINED: - return "UNDEFINED"; - default: - { - static char s[20]; - _mesa_snprintf(s, sizeof(s), "FILE%u", f); - return s; - } - } -} - - -/** - * Return ARB_v/f_prog-style input attrib string. - */ -static const char * -arb_input_attrib_string(GLint index, GLenum progType) -{ - /* - * These strings should match the VERT_ATTRIB_x and FRAG_ATTRIB_x tokens. - */ - const char *vertAttribs[] = { - "vertex.position", - "vertex.weight", - "vertex.normal", - "vertex.color.primary", - "vertex.color.secondary", - "vertex.fogcoord", - "vertex.(six)", - "vertex.(seven)", - "vertex.texcoord[0]", - "vertex.texcoord[1]", - "vertex.texcoord[2]", - "vertex.texcoord[3]", - "vertex.texcoord[4]", - "vertex.texcoord[5]", - "vertex.texcoord[6]", - "vertex.texcoord[7]", - "vertex.attrib[0]", - "vertex.attrib[1]", - "vertex.attrib[2]", - "vertex.attrib[3]", - "vertex.attrib[4]", - "vertex.attrib[5]", - "vertex.attrib[6]", - "vertex.attrib[7]", - "vertex.attrib[8]", - "vertex.attrib[9]", - "vertex.attrib[10]", - "vertex.attrib[11]", - "vertex.attrib[12]", - "vertex.attrib[13]", - "vertex.attrib[14]", - "vertex.attrib[15]" - }; - const char *fragAttribs[] = { - "fragment.position", - "fragment.color.primary", - "fragment.color.secondary", - "fragment.fogcoord", - "fragment.texcoord[0]", - "fragment.texcoord[1]", - "fragment.texcoord[2]", - "fragment.texcoord[3]", - "fragment.texcoord[4]", - "fragment.texcoord[5]", - "fragment.texcoord[6]", - "fragment.texcoord[7]", - "fragment.varying[0]", - "fragment.varying[1]", - "fragment.varying[2]", - "fragment.varying[3]", - "fragment.varying[4]", - "fragment.varying[5]", - "fragment.varying[6]", - "fragment.varying[7]" - }; - - /* sanity checks */ - assert(strcmp(vertAttribs[VERT_ATTRIB_TEX0], "vertex.texcoord[0]") == 0); - assert(strcmp(vertAttribs[VERT_ATTRIB_GENERIC15], "vertex.attrib[15]") == 0); - - if (progType == GL_VERTEX_PROGRAM_ARB) { - assert(index < sizeof(vertAttribs) / sizeof(vertAttribs[0])); - return vertAttribs[index]; - } - else { - assert(index < sizeof(fragAttribs) / sizeof(fragAttribs[0])); - return fragAttribs[index]; - } -} - - -/** - * Print a vertex program's InputsRead field in human-readable format. - * For debugging. - */ -void -_mesa_print_vp_inputs(GLbitfield inputs) -{ - printf("VP Inputs 0x%x: \n", inputs); - while (inputs) { - GLint attr = _mesa_ffs(inputs) - 1; - const char *name = arb_input_attrib_string(attr, - GL_VERTEX_PROGRAM_ARB); - printf(" %d: %s\n", attr, name); - inputs &= ~(1 << attr); - } -} - - -/** - * Print a fragment program's InputsRead field in human-readable format. - * For debugging. - */ -void -_mesa_print_fp_inputs(GLbitfield inputs) -{ - printf("FP Inputs 0x%x: \n", inputs); - while (inputs) { - GLint attr = _mesa_ffs(inputs) - 1; - const char *name = arb_input_attrib_string(attr, - GL_FRAGMENT_PROGRAM_ARB); - printf(" %d: %s\n", attr, name); - inputs &= ~(1 << attr); - } -} - - - -/** - * Return ARB_v/f_prog-style output attrib string. - */ -static const char * -arb_output_attrib_string(GLint index, GLenum progType) -{ - /* - * These strings should match the VERT_RESULT_x and FRAG_RESULT_x tokens. - */ - const char *vertResults[] = { - "result.position", - "result.color.primary", - "result.color.secondary", - "result.fogcoord", - "result.texcoord[0]", - "result.texcoord[1]", - "result.texcoord[2]", - "result.texcoord[3]", - "result.texcoord[4]", - "result.texcoord[5]", - "result.texcoord[6]", - "result.texcoord[7]", - "result.varying[0]", - "result.varying[1]", - "result.varying[2]", - "result.varying[3]", - "result.varying[4]", - "result.varying[5]", - "result.varying[6]", - "result.varying[7]" - }; - const char *fragResults[] = { - "result.color", - "result.color(half)", - "result.depth", - "result.color[0]", - "result.color[1]", - "result.color[2]", - "result.color[3]" - }; - - if (progType == GL_VERTEX_PROGRAM_ARB) { - assert(index < sizeof(vertResults) / sizeof(vertResults[0])); - return vertResults[index]; - } - else { - assert(index < sizeof(fragResults) / sizeof(fragResults[0])); - return fragResults[index]; - } -} - - -/** - * Return string representation of the given register. - * Note that some types of registers (like PROGRAM_UNIFORM) aren't defined - * by the ARB/NV program languages so we've taken some liberties here. - * \param f the register file (PROGRAM_INPUT, PROGRAM_TEMPORARY, etc) - * \param index number of the register in the register file - * \param mode the output format/mode/style - * \param prog pointer to containing program - */ -static const char * -reg_string(gl_register_file f, GLint index, gl_prog_print_mode mode, - GLboolean relAddr, const struct gl_program *prog, - GLboolean hasIndex2, GLboolean relAddr2, GLint index2) -{ - static char str[100]; - const char *addr = relAddr ? "ADDR+" : ""; - - str[0] = 0; - - switch (mode) { - case PROG_PRINT_DEBUG: - sprintf(str, "%s[%s%d]", - _mesa_register_file_name(f), addr, index); - if (hasIndex2) { - int offset = strlen(str); - const char *addr2 = relAddr2 ? "ADDR+" : ""; - sprintf(str+offset, "[%s%d]", addr2, index2); - } - break; - - case PROG_PRINT_ARB: - switch (f) { - case PROGRAM_INPUT: - sprintf(str, "%s", arb_input_attrib_string(index, prog->Target)); - break; - case PROGRAM_OUTPUT: - sprintf(str, "%s", arb_output_attrib_string(index, prog->Target)); - break; - case PROGRAM_TEMPORARY: - sprintf(str, "temp%d", index); - break; - case PROGRAM_ENV_PARAM: - sprintf(str, "program.env[%s%d]", addr, index); - break; - case PROGRAM_LOCAL_PARAM: - sprintf(str, "program.local[%s%d]", addr, index); - break; - case PROGRAM_VARYING: /* extension */ - sprintf(str, "varying[%s%d]", addr, index); - break; - case PROGRAM_CONSTANT: /* extension */ - sprintf(str, "constant[%s%d]", addr, index); - break; - case PROGRAM_UNIFORM: /* extension */ - sprintf(str, "uniform[%s%d]", addr, index); - break; - case PROGRAM_SYSTEM_VALUE: - sprintf(str, "sysvalue[%s%d]", addr, index); - break; - case PROGRAM_STATE_VAR: - { - struct gl_program_parameter *param - = prog->Parameters->Parameters + index; - char *state = _mesa_program_state_string(param->StateIndexes); - sprintf(str, "%s", state); - free(state); - } - break; - case PROGRAM_ADDRESS: - sprintf(str, "A%d", index); - break; - default: - _mesa_problem(NULL, "bad file in reg_string()"); - } - break; - - case PROG_PRINT_NV: - switch (f) { - case PROGRAM_INPUT: - if (prog->Target == GL_VERTEX_PROGRAM_ARB) - sprintf(str, "v[%d]", index); - else - sprintf(str, "f[%d]", index); - break; - case PROGRAM_OUTPUT: - sprintf(str, "o[%d]", index); - break; - case PROGRAM_TEMPORARY: - sprintf(str, "R%d", index); - break; - case PROGRAM_ENV_PARAM: - sprintf(str, "c[%d]", index); - break; - case PROGRAM_VARYING: /* extension */ - sprintf(str, "varying[%s%d]", addr, index); - break; - case PROGRAM_UNIFORM: /* extension */ - sprintf(str, "uniform[%s%d]", addr, index); - break; - case PROGRAM_CONSTANT: /* extension */ - sprintf(str, "constant[%s%d]", addr, index); - break; - case PROGRAM_STATE_VAR: /* extension */ - sprintf(str, "state[%s%d]", addr, index); - break; - default: - _mesa_problem(NULL, "bad file in reg_string()"); - } - break; - - default: - _mesa_problem(NULL, "bad mode in reg_string()"); - } - - return str; -} - - -/** - * Return a string representation of the given swizzle word. - * If extended is true, use extended (comma-separated) format. - * \param swizzle the swizzle field - * \param negateBase 4-bit negation vector - * \param extended if true, also allow 0, 1 values - */ -const char * -_mesa_swizzle_string(GLuint swizzle, GLuint negateMask, GLboolean extended) -{ - static const char swz[] = "xyzw01!?"; /* See SWIZZLE_x definitions */ - static char s[20]; - GLuint i = 0; - - if (!extended && swizzle == SWIZZLE_NOOP && negateMask == 0) - return ""; /* no swizzle/negation */ - - if (!extended) - s[i++] = '.'; - - if (negateMask & NEGATE_X) - s[i++] = '-'; - s[i++] = swz[GET_SWZ(swizzle, 0)]; - - if (extended) { - s[i++] = ','; - } - - if (negateMask & NEGATE_Y) - s[i++] = '-'; - s[i++] = swz[GET_SWZ(swizzle, 1)]; - - if (extended) { - s[i++] = ','; - } - - if (negateMask & NEGATE_Z) - s[i++] = '-'; - s[i++] = swz[GET_SWZ(swizzle, 2)]; - - if (extended) { - s[i++] = ','; - } - - if (negateMask & NEGATE_W) - s[i++] = '-'; - s[i++] = swz[GET_SWZ(swizzle, 3)]; - - s[i] = 0; - return s; -} - - -void -_mesa_print_swizzle(GLuint swizzle) -{ - if (swizzle == SWIZZLE_XYZW) { - printf(".xyzw\n"); - } - else { - const char *s = _mesa_swizzle_string(swizzle, 0, 0); - printf("%s\n", s); - } -} - - -const char * -_mesa_writemask_string(GLuint writeMask) -{ - static char s[10]; - GLuint i = 0; - - if (writeMask == WRITEMASK_XYZW) - return ""; - - s[i++] = '.'; - if (writeMask & WRITEMASK_X) - s[i++] = 'x'; - if (writeMask & WRITEMASK_Y) - s[i++] = 'y'; - if (writeMask & WRITEMASK_Z) - s[i++] = 'z'; - if (writeMask & WRITEMASK_W) - s[i++] = 'w'; - - s[i] = 0; - return s; -} - - -const char * -_mesa_condcode_string(GLuint condcode) -{ - switch (condcode) { - case COND_GT: return "GT"; - case COND_EQ: return "EQ"; - case COND_LT: return "LT"; - case COND_UN: return "UN"; - case COND_GE: return "GE"; - case COND_LE: return "LE"; - case COND_NE: return "NE"; - case COND_TR: return "TR"; - case COND_FL: return "FL"; - default: return "cond???"; - } -} - - -static void -fprint_dst_reg(FILE * f, - const struct prog_dst_register *dstReg, - gl_prog_print_mode mode, - const struct gl_program *prog) -{ - fprintf(f, "%s%s", - reg_string((gl_register_file) dstReg->File, - dstReg->Index, mode, dstReg->RelAddr, prog, - GL_FALSE, GL_FALSE, 0), - _mesa_writemask_string(dstReg->WriteMask)); - - if (dstReg->CondMask != COND_TR) { - fprintf(f, " (%s.%s)", - _mesa_condcode_string(dstReg->CondMask), - _mesa_swizzle_string(dstReg->CondSwizzle, - GL_FALSE, GL_FALSE)); - } - -#if 0 - fprintf(f, "%s[%d]%s", - _mesa_register_file_name((gl_register_file) dstReg->File), - dstReg->Index, - _mesa_writemask_string(dstReg->WriteMask)); -#endif -} - - -static void -fprint_src_reg(FILE *f, - const struct prog_src_register *srcReg, - gl_prog_print_mode mode, - const struct gl_program *prog) -{ - const char *abs = srcReg->Abs ? "|" : ""; - - fprintf(f, "%s%s%s%s", - abs, - reg_string((gl_register_file) srcReg->File, - srcReg->Index, mode, srcReg->RelAddr, prog, - srcReg->HasIndex2, srcReg->RelAddr2, srcReg->Index2), - _mesa_swizzle_string(srcReg->Swizzle, - srcReg->Negate, GL_FALSE), - abs); -#if 0 - fprintf(f, "%s[%d]%s", - _mesa_register_file_name((gl_register_file) srcReg->File), - srcReg->Index, - _mesa_swizzle_string(srcReg->Swizzle, - srcReg->Negate, GL_FALSE)); -#endif -} - - -static void -fprint_comment(FILE *f, const struct prog_instruction *inst) -{ - if (inst->Comment) - fprintf(f, "; # %s\n", inst->Comment); - else - fprintf(f, ";\n"); -} - - -void -_mesa_fprint_alu_instruction(FILE *f, - const struct prog_instruction *inst, - const char *opcode_string, GLuint numRegs, - gl_prog_print_mode mode, - const struct gl_program *prog) -{ - GLuint j; - - fprintf(f, "%s", opcode_string); - if (inst->CondUpdate) - fprintf(f, ".C"); - - /* frag prog only */ - if (inst->SaturateMode == SATURATE_ZERO_ONE) - fprintf(f, "_SAT"); - - fprintf(f, " "); - if (inst->DstReg.File != PROGRAM_UNDEFINED) { - fprint_dst_reg(f, &inst->DstReg, mode, prog); - } - else { - fprintf(f, " ???"); - } - - if (numRegs > 0) - fprintf(f, ", "); - - for (j = 0; j < numRegs; j++) { - fprint_src_reg(f, inst->SrcReg + j, mode, prog); - if (j + 1 < numRegs) - fprintf(f, ", "); - } - - fprint_comment(f, inst); -} - - -void -_mesa_print_alu_instruction(const struct prog_instruction *inst, - const char *opcode_string, GLuint numRegs) -{ - _mesa_fprint_alu_instruction(stderr, inst, opcode_string, - numRegs, PROG_PRINT_DEBUG, NULL); -} - - -/** - * Print a single vertex/fragment program instruction. - */ -GLint -_mesa_fprint_instruction_opt(FILE *f, - const struct prog_instruction *inst, - GLint indent, - gl_prog_print_mode mode, - const struct gl_program *prog) -{ - GLint i; - - if (inst->Opcode == OPCODE_ELSE || - inst->Opcode == OPCODE_ENDIF || - inst->Opcode == OPCODE_ENDLOOP || - inst->Opcode == OPCODE_ENDSUB) { - indent -= 3; - } - for (i = 0; i < indent; i++) { - fprintf(f, " "); - } - - switch (inst->Opcode) { - case OPCODE_PRINT: - fprintf(f, "PRINT '%s'", (char *) inst->Data); - if (inst->SrcReg[0].File != PROGRAM_UNDEFINED) { - fprintf(f, ", "); - fprintf(f, "%s[%d]%s", - _mesa_register_file_name((gl_register_file) inst->SrcReg[0].File), - inst->SrcReg[0].Index, - _mesa_swizzle_string(inst->SrcReg[0].Swizzle, - inst->SrcReg[0].Negate, GL_FALSE)); - } - if (inst->Comment) - fprintf(f, " # %s", inst->Comment); - fprint_comment(f, inst); - break; - case OPCODE_SWZ: - fprintf(f, "SWZ"); - if (inst->SaturateMode == SATURATE_ZERO_ONE) - fprintf(f, "_SAT"); - fprintf(f, " "); - fprint_dst_reg(f, &inst->DstReg, mode, prog); - fprintf(f, ", %s[%d], %s", - _mesa_register_file_name((gl_register_file) inst->SrcReg[0].File), - inst->SrcReg[0].Index, - _mesa_swizzle_string(inst->SrcReg[0].Swizzle, - inst->SrcReg[0].Negate, GL_TRUE)); - fprint_comment(f, inst); - break; - case OPCODE_TEX: - case OPCODE_TXP: - case OPCODE_TXL: - case OPCODE_TXB: - case OPCODE_TXD: - fprintf(f, "%s", _mesa_opcode_string(inst->Opcode)); - if (inst->SaturateMode == SATURATE_ZERO_ONE) - fprintf(f, "_SAT"); - fprintf(f, " "); - fprint_dst_reg(f, &inst->DstReg, mode, prog); - fprintf(f, ", "); - fprint_src_reg(f, &inst->SrcReg[0], mode, prog); - if (inst->Opcode == OPCODE_TXD) { - fprintf(f, ", "); - fprint_src_reg(f, &inst->SrcReg[1], mode, prog); - fprintf(f, ", "); - fprint_src_reg(f, &inst->SrcReg[2], mode, prog); - } - fprintf(f, ", texture[%d], ", inst->TexSrcUnit); - switch (inst->TexSrcTarget) { - case TEXTURE_1D_INDEX: fprintf(f, "1D"); break; - case TEXTURE_2D_INDEX: fprintf(f, "2D"); break; - case TEXTURE_3D_INDEX: fprintf(f, "3D"); break; - case TEXTURE_CUBE_INDEX: fprintf(f, "CUBE"); break; - case TEXTURE_RECT_INDEX: fprintf(f, "RECT"); break; - case TEXTURE_1D_ARRAY_INDEX: fprintf(f, "1D_ARRAY"); break; - case TEXTURE_2D_ARRAY_INDEX: fprintf(f, "2D_ARRAY"); break; - default: - ; - } - if (inst->TexShadow) - fprintf(f, " SHADOW"); - fprint_comment(f, inst); - break; - - case OPCODE_KIL: - fprintf(f, "%s", _mesa_opcode_string(inst->Opcode)); - fprintf(f, " "); - fprint_src_reg(f, &inst->SrcReg[0], mode, prog); - fprint_comment(f, inst); - break; - case OPCODE_KIL_NV: - fprintf(f, "%s", _mesa_opcode_string(inst->Opcode)); - fprintf(f, " "); - fprintf(f, "%s.%s", - _mesa_condcode_string(inst->DstReg.CondMask), - _mesa_swizzle_string(inst->DstReg.CondSwizzle, - GL_FALSE, GL_FALSE)); - fprint_comment(f, inst); - break; - - case OPCODE_ARL: - fprintf(f, "ARL "); - fprint_dst_reg(f, &inst->DstReg, mode, prog); - fprintf(f, ", "); - fprint_src_reg(f, &inst->SrcReg[0], mode, prog); - fprint_comment(f, inst); - break; - case OPCODE_BRA: - fprintf(f, "BRA %d (%s%s)", - inst->BranchTarget, - _mesa_condcode_string(inst->DstReg.CondMask), - _mesa_swizzle_string(inst->DstReg.CondSwizzle, 0, GL_FALSE)); - fprint_comment(f, inst); - break; - case OPCODE_IF: - if (inst->SrcReg[0].File != PROGRAM_UNDEFINED) { - /* Use ordinary register */ - fprintf(f, "IF "); - fprint_src_reg(f, &inst->SrcReg[0], mode, prog); - fprintf(f, "; "); - } - else { - /* Use cond codes */ - fprintf(f, "IF (%s%s);", - _mesa_condcode_string(inst->DstReg.CondMask), - _mesa_swizzle_string(inst->DstReg.CondSwizzle, - 0, GL_FALSE)); - } - fprintf(f, " # (if false, goto %d)", inst->BranchTarget); - fprint_comment(f, inst); - return indent + 3; - case OPCODE_ELSE: - fprintf(f, "ELSE; # (goto %d)\n", inst->BranchTarget); - return indent + 3; - case OPCODE_ENDIF: - fprintf(f, "ENDIF;\n"); - break; - case OPCODE_BGNLOOP: - fprintf(f, "BGNLOOP; # (end at %d)\n", inst->BranchTarget); - return indent + 3; - case OPCODE_ENDLOOP: - fprintf(f, "ENDLOOP; # (goto %d)\n", inst->BranchTarget); - break; - case OPCODE_BRK: - case OPCODE_CONT: - fprintf(f, "%s (%s%s); # (goto %d)", - _mesa_opcode_string(inst->Opcode), - _mesa_condcode_string(inst->DstReg.CondMask), - _mesa_swizzle_string(inst->DstReg.CondSwizzle, 0, GL_FALSE), - inst->BranchTarget); - fprint_comment(f, inst); - break; - - case OPCODE_BGNSUB: - if (mode == PROG_PRINT_NV) { - fprintf(f, "%s:\n", inst->Comment); /* comment is label */ - return indent; - } - else { - fprintf(f, "BGNSUB"); - fprint_comment(f, inst); - return indent + 3; - } - case OPCODE_ENDSUB: - if (mode == PROG_PRINT_DEBUG) { - fprintf(f, "ENDSUB"); - fprint_comment(f, inst); - } - break; - case OPCODE_CAL: - if (mode == PROG_PRINT_NV) { - fprintf(f, "CAL %s; # (goto %d)\n", inst->Comment, inst->BranchTarget); - } - else { - fprintf(f, "CAL %u", inst->BranchTarget); - fprint_comment(f, inst); - } - break; - case OPCODE_RET: - fprintf(f, "RET (%s%s)", - _mesa_condcode_string(inst->DstReg.CondMask), - _mesa_swizzle_string(inst->DstReg.CondSwizzle, 0, GL_FALSE)); - fprint_comment(f, inst); - break; - - case OPCODE_END: - fprintf(f, "END\n"); - break; - case OPCODE_NOP: - if (mode == PROG_PRINT_DEBUG) { - fprintf(f, "NOP"); - fprint_comment(f, inst); - } - else if (inst->Comment) { - /* ARB/NV extensions don't have NOP instruction */ - fprintf(f, "# %s\n", inst->Comment); - } - break; - case OPCODE_EMIT_VERTEX: - fprintf(f, "EMIT_VERTEX\n"); - break; - case OPCODE_END_PRIMITIVE: - fprintf(f, "END_PRIMITIVE\n"); - break; - /* XXX may need other special-case instructions */ - default: - if (inst->Opcode < MAX_OPCODE) { - /* typical alu instruction */ - _mesa_fprint_alu_instruction(f, inst, - _mesa_opcode_string(inst->Opcode), - _mesa_num_inst_src_regs(inst->Opcode), - mode, prog); - } - else { - _mesa_fprint_alu_instruction(f, inst, - _mesa_opcode_string(inst->Opcode), - 3/*_mesa_num_inst_src_regs(inst->Opcode)*/, - mode, prog); - } - break; - } - return indent; -} - - -GLint -_mesa_print_instruction_opt(const struct prog_instruction *inst, - GLint indent, - gl_prog_print_mode mode, - const struct gl_program *prog) -{ - return _mesa_fprint_instruction_opt(stderr, inst, indent, mode, prog); -} - - -void -_mesa_print_instruction(const struct prog_instruction *inst) -{ - /* note: 4th param should be ignored for PROG_PRINT_DEBUG */ - _mesa_fprint_instruction_opt(stderr, inst, 0, PROG_PRINT_DEBUG, NULL); -} - - - -/** - * Print program, with options. - */ -void -_mesa_fprint_program_opt(FILE *f, - const struct gl_program *prog, - gl_prog_print_mode mode, - GLboolean lineNumbers) -{ - GLuint i, indent = 0; - - switch (prog->Target) { - case GL_VERTEX_PROGRAM_ARB: - if (mode == PROG_PRINT_ARB) - fprintf(f, "!!ARBvp1.0\n"); - else if (mode == PROG_PRINT_NV) - fprintf(f, "!!VP1.0\n"); - else - fprintf(f, "# Vertex Program/Shader %u\n", prog->Id); - break; - case GL_FRAGMENT_PROGRAM_ARB: - case GL_FRAGMENT_PROGRAM_NV: - if (mode == PROG_PRINT_ARB) - fprintf(f, "!!ARBfp1.0\n"); - else if (mode == PROG_PRINT_NV) - fprintf(f, "!!FP1.0\n"); - else - fprintf(f, "# Fragment Program/Shader %u\n", prog->Id); - break; - case MESA_GEOMETRY_PROGRAM: - fprintf(f, "# Geometry Shader\n"); - } - - for (i = 0; i < prog->NumInstructions; i++) { - if (lineNumbers) - fprintf(f, "%3d: ", i); - indent = _mesa_fprint_instruction_opt(f, prog->Instructions + i, - indent, mode, prog); - } -} - - -/** - * Print program to stderr, default options. - */ -void -_mesa_print_program(const struct gl_program *prog) -{ - _mesa_fprint_program_opt(stderr, prog, PROG_PRINT_DEBUG, GL_TRUE); -} - - -/** - * Return binary representation of 64-bit value (as a string). - * Insert a comma to separate each group of 8 bits. - * Note we return a pointer to local static storage so this is not - * re-entrant, etc. - * XXX move to imports.[ch] if useful elsewhere. - */ -static const char * -binary(GLbitfield64 val) -{ - static char buf[80]; - GLint i, len = 0; - for (i = 63; i >= 0; --i) { - if (val & (BITFIELD64_BIT(i))) - buf[len++] = '1'; - else if (len > 0 || i == 0) - buf[len++] = '0'; - if (len > 0 && ((i-1) % 8) == 7) - buf[len++] = ','; - } - buf[len] = '\0'; - return buf; -} - - -/** - * Print all of a program's parameters/fields to given file. - */ -static void -_mesa_fprint_program_parameters(FILE *f, - struct gl_context *ctx, - const struct gl_program *prog) -{ - GLuint i; - - fprintf(f, "InputsRead: 0x%x (0b%s)\n", - prog->InputsRead, binary(prog->InputsRead)); - fprintf(f, "OutputsWritten: 0x%llx (0b%s)\n", - (unsigned long long)prog->OutputsWritten, - binary(prog->OutputsWritten)); - fprintf(f, "NumInstructions=%d\n", prog->NumInstructions); - fprintf(f, "NumTemporaries=%d\n", prog->NumTemporaries); - fprintf(f, "NumParameters=%d\n", prog->NumParameters); - fprintf(f, "NumAttributes=%d\n", prog->NumAttributes); - fprintf(f, "NumAddressRegs=%d\n", prog->NumAddressRegs); - fprintf(f, "IndirectRegisterFiles: 0x%x (0b%s)\n", - prog->IndirectRegisterFiles, binary(prog->IndirectRegisterFiles)); - fprintf(f, "SamplersUsed: 0x%x (0b%s)\n", - prog->SamplersUsed, binary(prog->SamplersUsed)); - fprintf(f, "Samplers=[ "); - for (i = 0; i < MAX_SAMPLERS; i++) { - fprintf(f, "%d ", prog->SamplerUnits[i]); - } - fprintf(f, "]\n"); - - _mesa_load_state_parameters(ctx, prog->Parameters); - -#if 0 - fprintf(f, "Local Params:\n"); - for (i = 0; i < MAX_PROGRAM_LOCAL_PARAMS; i++){ - const GLfloat *p = prog->LocalParams[i]; - fprintf(f, "%2d: %f, %f, %f, %f\n", i, p[0], p[1], p[2], p[3]); - } -#endif - _mesa_print_parameter_list(prog->Parameters); -} - - -/** - * Print all of a program's parameters/fields to stderr. - */ -void -_mesa_print_program_parameters(struct gl_context *ctx, const struct gl_program *prog) -{ - _mesa_fprint_program_parameters(stderr, ctx, prog); -} - - -/** - * Print a program parameter list to given file. - */ -static void -_mesa_fprint_parameter_list(FILE *f, - const struct gl_program_parameter_list *list) -{ - GLuint i; - - if (!list) - return; - - if (0) - fprintf(f, "param list %p\n", (void *) list); - fprintf(f, "dirty state flags: 0x%x\n", list->StateFlags); - for (i = 0; i < list->NumParameters; i++){ - struct gl_program_parameter *param = list->Parameters + i; - const GLfloat *v = list->ParameterValues[i]; - fprintf(f, "param[%d] sz=%d %s %s = {%.3g, %.3g, %.3g, %.3g}", - i, param->Size, - _mesa_register_file_name(list->Parameters[i].Type), - param->Name, v[0], v[1], v[2], v[3]); - if (param->Flags & PROG_PARAM_BIT_CENTROID) - fprintf(f, " Centroid"); - if (param->Flags & PROG_PARAM_BIT_INVARIANT) - fprintf(f, " Invariant"); - if (param->Flags & PROG_PARAM_BIT_FLAT) - fprintf(f, " Flat"); - if (param->Flags & PROG_PARAM_BIT_LINEAR) - fprintf(f, " Linear"); - fprintf(f, "\n"); - } -} - - -/** - * Print a program parameter list to stderr. - */ -void -_mesa_print_parameter_list(const struct gl_program_parameter_list *list) -{ - _mesa_fprint_parameter_list(stderr, list); -} - - -/** - * Write shader and associated info to a file. - */ -void -_mesa_write_shader_to_file(const struct gl_shader *shader) -{ - const char *type; - char filename[100]; - FILE *f; - - if (shader->Type == GL_FRAGMENT_SHADER) - type = "frag"; - else if (shader->Type == GL_VERTEX_SHADER) - type = "vert"; - else - type = "geom"; - - _mesa_snprintf(filename, sizeof(filename), "shader_%u.%s", shader->Name, type); - f = fopen(filename, "w"); - if (!f) { - fprintf(stderr, "Unable to open %s for writing\n", filename); - return; - } - - fprintf(f, "/* Shader %u source, checksum %u */\n", shader->Name, shader->SourceChecksum); - fputs(shader->Source, f); - fprintf(f, "\n"); - - fprintf(f, "/* Compile status: %s */\n", - shader->CompileStatus ? "ok" : "fail"); - fprintf(f, "/* Log Info: */\n"); - if (shader->InfoLog) { - fputs(shader->InfoLog, f); - } - if (shader->CompileStatus && shader->Program) { - fprintf(f, "/* GPU code */\n"); - fprintf(f, "/*\n"); - _mesa_fprint_program_opt(f, shader->Program, PROG_PRINT_DEBUG, GL_TRUE); - fprintf(f, "*/\n"); - fprintf(f, "/* Parameters / constants */\n"); - fprintf(f, "/*\n"); - _mesa_fprint_parameter_list(f, shader->Program->Parameters); - fprintf(f, "*/\n"); - } - - fclose(f); -} - - -/** - * Append the shader's uniform info/values to the shader log file. - * The log file will typically have been created by the - * _mesa_write_shader_to_file function. - */ -void -_mesa_append_uniforms_to_file(const struct gl_shader *shader, - const struct gl_program *prog) -{ - const char *type; - char filename[100]; - FILE *f; - - if (shader->Type == GL_FRAGMENT_SHADER) - type = "frag"; - else - type = "vert"; - - _mesa_snprintf(filename, sizeof(filename), "shader_%u.%s", shader->Name, type); - f = fopen(filename, "a"); /* append */ - if (!f) { - fprintf(stderr, "Unable to open %s for appending\n", filename); - return; - } - - fprintf(f, "/* First-draw parameters / constants */\n"); - fprintf(f, "/*\n"); - _mesa_fprint_parameter_list(f, prog->Parameters); - fprintf(f, "*/\n"); - - fclose(f); -} +/*
+ * Mesa 3-D graphics library
+ * Version: 7.3
+ *
+ * Copyright (C) 1999-2008 Brian Paul All Rights Reserved.
+ * Copyright (C) 2009 VMware, Inc. 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_print.c
+ * Print vertex/fragment programs - for debugging.
+ * \author Brian Paul
+ */
+
+#include "main/glheader.h"
+#include "main/context.h"
+#include "main/imports.h"
+#include "prog_instruction.h"
+#include "prog_parameter.h"
+#include "prog_print.h"
+#include "prog_statevars.h"
+
+
+
+/**
+ * Return string name for given program/register file.
+ */
+const char *
+_mesa_register_file_name(gl_register_file f)
+{
+ switch (f) {
+ case PROGRAM_TEMPORARY:
+ return "TEMP";
+ case PROGRAM_LOCAL_PARAM:
+ return "LOCAL";
+ case PROGRAM_ENV_PARAM:
+ return "ENV";
+ case PROGRAM_STATE_VAR:
+ return "STATE";
+ case PROGRAM_INPUT:
+ return "INPUT";
+ case PROGRAM_OUTPUT:
+ return "OUTPUT";
+ case PROGRAM_NAMED_PARAM:
+ return "NAMED";
+ case PROGRAM_CONSTANT:
+ return "CONST";
+ case PROGRAM_UNIFORM:
+ return "UNIFORM";
+ case PROGRAM_VARYING:
+ return "VARYING";
+ case PROGRAM_WRITE_ONLY:
+ return "WRITE_ONLY";
+ case PROGRAM_ADDRESS:
+ return "ADDR";
+ case PROGRAM_SAMPLER:
+ return "SAMPLER";
+ case PROGRAM_SYSTEM_VALUE:
+ return "SYSVAL";
+ case PROGRAM_UNDEFINED:
+ return "UNDEFINED";
+ default:
+ {
+ static char s[20];
+ _mesa_snprintf(s, sizeof(s), "FILE%u", f);
+ return s;
+ }
+ }
+}
+
+
+/**
+ * Return ARB_v/f_prog-style input attrib string.
+ */
+static const char *
+arb_input_attrib_string(GLint index, GLenum progType)
+{
+ /*
+ * These strings should match the VERT_ATTRIB_x and FRAG_ATTRIB_x tokens.
+ */
+ const char *vertAttribs[] = {
+ "vertex.position",
+ "vertex.weight",
+ "vertex.normal",
+ "vertex.color.primary",
+ "vertex.color.secondary",
+ "vertex.fogcoord",
+ "vertex.(six)",
+ "vertex.(seven)",
+ "vertex.texcoord[0]",
+ "vertex.texcoord[1]",
+ "vertex.texcoord[2]",
+ "vertex.texcoord[3]",
+ "vertex.texcoord[4]",
+ "vertex.texcoord[5]",
+ "vertex.texcoord[6]",
+ "vertex.texcoord[7]",
+ "vertex.attrib[0]",
+ "vertex.attrib[1]",
+ "vertex.attrib[2]",
+ "vertex.attrib[3]",
+ "vertex.attrib[4]",
+ "vertex.attrib[5]",
+ "vertex.attrib[6]",
+ "vertex.attrib[7]",
+ "vertex.attrib[8]",
+ "vertex.attrib[9]",
+ "vertex.attrib[10]",
+ "vertex.attrib[11]",
+ "vertex.attrib[12]",
+ "vertex.attrib[13]",
+ "vertex.attrib[14]",
+ "vertex.attrib[15]"
+ };
+ const char *fragAttribs[] = {
+ "fragment.position",
+ "fragment.color.primary",
+ "fragment.color.secondary",
+ "fragment.fogcoord",
+ "fragment.texcoord[0]",
+ "fragment.texcoord[1]",
+ "fragment.texcoord[2]",
+ "fragment.texcoord[3]",
+ "fragment.texcoord[4]",
+ "fragment.texcoord[5]",
+ "fragment.texcoord[6]",
+ "fragment.texcoord[7]",
+ "fragment.varying[0]",
+ "fragment.varying[1]",
+ "fragment.varying[2]",
+ "fragment.varying[3]",
+ "fragment.varying[4]",
+ "fragment.varying[5]",
+ "fragment.varying[6]",
+ "fragment.varying[7]"
+ };
+
+ /* sanity checks */
+ assert(strcmp(vertAttribs[VERT_ATTRIB_TEX0], "vertex.texcoord[0]") == 0);
+ assert(strcmp(vertAttribs[VERT_ATTRIB_GENERIC15], "vertex.attrib[15]") == 0);
+
+ if (progType == GL_VERTEX_PROGRAM_ARB) {
+ assert(index < sizeof(vertAttribs) / sizeof(vertAttribs[0]));
+ return vertAttribs[index];
+ }
+ else {
+ assert(index < sizeof(fragAttribs) / sizeof(fragAttribs[0]));
+ return fragAttribs[index];
+ }
+}
+
+
+/**
+ * Print a vertex program's InputsRead field in human-readable format.
+ * For debugging.
+ */
+void
+_mesa_print_vp_inputs(GLbitfield inputs)
+{
+ printf("VP Inputs 0x%x: \n", inputs);
+ while (inputs) {
+ GLint attr = _mesa_ffs(inputs) - 1;
+ const char *name = arb_input_attrib_string(attr,
+ GL_VERTEX_PROGRAM_ARB);
+ printf(" %d: %s\n", attr, name);
+ inputs &= ~(1 << attr);
+ }
+}
+
+
+/**
+ * Print a fragment program's InputsRead field in human-readable format.
+ * For debugging.
+ */
+void
+_mesa_print_fp_inputs(GLbitfield inputs)
+{
+ printf("FP Inputs 0x%x: \n", inputs);
+ while (inputs) {
+ GLint attr = _mesa_ffs(inputs) - 1;
+ const char *name = arb_input_attrib_string(attr,
+ GL_FRAGMENT_PROGRAM_ARB);
+ printf(" %d: %s\n", attr, name);
+ inputs &= ~(1 << attr);
+ }
+}
+
+
+
+/**
+ * Return ARB_v/f_prog-style output attrib string.
+ */
+static const char *
+arb_output_attrib_string(GLint index, GLenum progType)
+{
+ /*
+ * These strings should match the VERT_RESULT_x and FRAG_RESULT_x tokens.
+ */
+ const char *vertResults[] = {
+ "result.position",
+ "result.color.primary",
+ "result.color.secondary",
+ "result.fogcoord",
+ "result.texcoord[0]",
+ "result.texcoord[1]",
+ "result.texcoord[2]",
+ "result.texcoord[3]",
+ "result.texcoord[4]",
+ "result.texcoord[5]",
+ "result.texcoord[6]",
+ "result.texcoord[7]",
+ "result.varying[0]",
+ "result.varying[1]",
+ "result.varying[2]",
+ "result.varying[3]",
+ "result.varying[4]",
+ "result.varying[5]",
+ "result.varying[6]",
+ "result.varying[7]"
+ };
+ const char *fragResults[] = {
+ "result.color",
+ "result.color(half)",
+ "result.depth",
+ "result.color[0]",
+ "result.color[1]",
+ "result.color[2]",
+ "result.color[3]"
+ };
+
+ if (progType == GL_VERTEX_PROGRAM_ARB) {
+ assert(index < sizeof(vertResults) / sizeof(vertResults[0]));
+ return vertResults[index];
+ }
+ else {
+ assert(index < sizeof(fragResults) / sizeof(fragResults[0]));
+ return fragResults[index];
+ }
+}
+
+
+/**
+ * Return string representation of the given register.
+ * Note that some types of registers (like PROGRAM_UNIFORM) aren't defined
+ * by the ARB/NV program languages so we've taken some liberties here.
+ * \param f the register file (PROGRAM_INPUT, PROGRAM_TEMPORARY, etc)
+ * \param index number of the register in the register file
+ * \param mode the output format/mode/style
+ * \param prog pointer to containing program
+ */
+static const char *
+reg_string(gl_register_file f, GLint index, gl_prog_print_mode mode,
+ GLboolean relAddr, const struct gl_program *prog,
+ GLboolean hasIndex2, GLboolean relAddr2, GLint index2)
+{
+ static char str[100];
+ const char *addr = relAddr ? "ADDR+" : "";
+
+ str[0] = 0;
+
+ switch (mode) {
+ case PROG_PRINT_DEBUG:
+ sprintf(str, "%s[%s%d]",
+ _mesa_register_file_name(f), addr, index);
+ if (hasIndex2) {
+ int offset = strlen(str);
+ const char *addr2 = relAddr2 ? "ADDR+" : "";
+ sprintf(str+offset, "[%s%d]", addr2, index2);
+ }
+ break;
+
+ case PROG_PRINT_ARB:
+ switch (f) {
+ case PROGRAM_INPUT:
+ sprintf(str, "%s", arb_input_attrib_string(index, prog->Target));
+ break;
+ case PROGRAM_OUTPUT:
+ sprintf(str, "%s", arb_output_attrib_string(index, prog->Target));
+ break;
+ case PROGRAM_TEMPORARY:
+ sprintf(str, "temp%d", index);
+ break;
+ case PROGRAM_ENV_PARAM:
+ sprintf(str, "program.env[%s%d]", addr, index);
+ break;
+ case PROGRAM_LOCAL_PARAM:
+ sprintf(str, "program.local[%s%d]", addr, index);
+ break;
+ case PROGRAM_VARYING: /* extension */
+ sprintf(str, "varying[%s%d]", addr, index);
+ break;
+ case PROGRAM_CONSTANT: /* extension */
+ sprintf(str, "constant[%s%d]", addr, index);
+ break;
+ case PROGRAM_UNIFORM: /* extension */
+ sprintf(str, "uniform[%s%d]", addr, index);
+ break;
+ case PROGRAM_SYSTEM_VALUE:
+ sprintf(str, "sysvalue[%s%d]", addr, index);
+ break;
+ case PROGRAM_STATE_VAR:
+ {
+ struct gl_program_parameter *param
+ = prog->Parameters->Parameters + index;
+ char *state = _mesa_program_state_string(param->StateIndexes);
+ sprintf(str, "%s", state);
+ free(state);
+ }
+ break;
+ case PROGRAM_ADDRESS:
+ sprintf(str, "A%d", index);
+ break;
+ default:
+ _mesa_problem(NULL, "bad file in reg_string()");
+ }
+ break;
+
+ case PROG_PRINT_NV:
+ switch (f) {
+ case PROGRAM_INPUT:
+ if (prog->Target == GL_VERTEX_PROGRAM_ARB)
+ sprintf(str, "v[%d]", index);
+ else
+ sprintf(str, "f[%d]", index);
+ break;
+ case PROGRAM_OUTPUT:
+ sprintf(str, "o[%d]", index);
+ break;
+ case PROGRAM_TEMPORARY:
+ sprintf(str, "R%d", index);
+ break;
+ case PROGRAM_ENV_PARAM:
+ sprintf(str, "c[%d]", index);
+ break;
+ case PROGRAM_VARYING: /* extension */
+ sprintf(str, "varying[%s%d]", addr, index);
+ break;
+ case PROGRAM_UNIFORM: /* extension */
+ sprintf(str, "uniform[%s%d]", addr, index);
+ break;
+ case PROGRAM_CONSTANT: /* extension */
+ sprintf(str, "constant[%s%d]", addr, index);
+ break;
+ case PROGRAM_STATE_VAR: /* extension */
+ sprintf(str, "state[%s%d]", addr, index);
+ break;
+ default:
+ _mesa_problem(NULL, "bad file in reg_string()");
+ }
+ break;
+
+ default:
+ _mesa_problem(NULL, "bad mode in reg_string()");
+ }
+
+ return str;
+}
+
+
+/**
+ * Return a string representation of the given swizzle word.
+ * If extended is true, use extended (comma-separated) format.
+ * \param swizzle the swizzle field
+ * \param negateBase 4-bit negation vector
+ * \param extended if true, also allow 0, 1 values
+ */
+const char *
+_mesa_swizzle_string(GLuint swizzle, GLuint negateMask, GLboolean extended)
+{
+ static const char swz[] = "xyzw01!?"; /* See SWIZZLE_x definitions */
+ static char s[20];
+ GLuint i = 0;
+
+ if (!extended && swizzle == SWIZZLE_NOOP && negateMask == 0)
+ return ""; /* no swizzle/negation */
+
+ if (!extended)
+ s[i++] = '.';
+
+ if (negateMask & NEGATE_X)
+ s[i++] = '-';
+ s[i++] = swz[GET_SWZ(swizzle, 0)];
+
+ if (extended) {
+ s[i++] = ',';
+ }
+
+ if (negateMask & NEGATE_Y)
+ s[i++] = '-';
+ s[i++] = swz[GET_SWZ(swizzle, 1)];
+
+ if (extended) {
+ s[i++] = ',';
+ }
+
+ if (negateMask & NEGATE_Z)
+ s[i++] = '-';
+ s[i++] = swz[GET_SWZ(swizzle, 2)];
+
+ if (extended) {
+ s[i++] = ',';
+ }
+
+ if (negateMask & NEGATE_W)
+ s[i++] = '-';
+ s[i++] = swz[GET_SWZ(swizzle, 3)];
+
+ s[i] = 0;
+ return s;
+}
+
+
+void
+_mesa_print_swizzle(GLuint swizzle)
+{
+ if (swizzle == SWIZZLE_XYZW) {
+ printf(".xyzw\n");
+ }
+ else {
+ const char *s = _mesa_swizzle_string(swizzle, 0, 0);
+ printf("%s\n", s);
+ }
+}
+
+
+const char *
+_mesa_writemask_string(GLuint writeMask)
+{
+ static char s[10];
+ GLuint i = 0;
+
+ if (writeMask == WRITEMASK_XYZW)
+ return "";
+
+ s[i++] = '.';
+ if (writeMask & WRITEMASK_X)
+ s[i++] = 'x';
+ if (writeMask & WRITEMASK_Y)
+ s[i++] = 'y';
+ if (writeMask & WRITEMASK_Z)
+ s[i++] = 'z';
+ if (writeMask & WRITEMASK_W)
+ s[i++] = 'w';
+
+ s[i] = 0;
+ return s;
+}
+
+
+const char *
+_mesa_condcode_string(GLuint condcode)
+{
+ switch (condcode) {
+ case COND_GT: return "GT";
+ case COND_EQ: return "EQ";
+ case COND_LT: return "LT";
+ case COND_UN: return "UN";
+ case COND_GE: return "GE";
+ case COND_LE: return "LE";
+ case COND_NE: return "NE";
+ case COND_TR: return "TR";
+ case COND_FL: return "FL";
+ default: return "cond???";
+ }
+}
+
+
+static void
+fprint_dst_reg(FILE * f,
+ const struct prog_dst_register *dstReg,
+ gl_prog_print_mode mode,
+ const struct gl_program *prog)
+{
+ fprintf(f, "%s%s",
+ reg_string((gl_register_file) dstReg->File,
+ dstReg->Index, mode, dstReg->RelAddr, prog,
+ GL_FALSE, GL_FALSE, 0),
+ _mesa_writemask_string(dstReg->WriteMask));
+
+ if (dstReg->CondMask != COND_TR) {
+ fprintf(f, " (%s.%s)",
+ _mesa_condcode_string(dstReg->CondMask),
+ _mesa_swizzle_string(dstReg->CondSwizzle,
+ GL_FALSE, GL_FALSE));
+ }
+
+#if 0
+ fprintf(f, "%s[%d]%s",
+ _mesa_register_file_name((gl_register_file) dstReg->File),
+ dstReg->Index,
+ _mesa_writemask_string(dstReg->WriteMask));
+#endif
+}
+
+
+static void
+fprint_src_reg(FILE *f,
+ const struct prog_src_register *srcReg,
+ gl_prog_print_mode mode,
+ const struct gl_program *prog)
+{
+ const char *abs = srcReg->Abs ? "|" : "";
+
+ fprintf(f, "%s%s%s%s",
+ abs,
+ reg_string((gl_register_file) srcReg->File,
+ srcReg->Index, mode, srcReg->RelAddr, prog,
+ srcReg->HasIndex2, srcReg->RelAddr2, srcReg->Index2),
+ _mesa_swizzle_string(srcReg->Swizzle,
+ srcReg->Negate, GL_FALSE),
+ abs);
+#if 0
+ fprintf(f, "%s[%d]%s",
+ _mesa_register_file_name((gl_register_file) srcReg->File),
+ srcReg->Index,
+ _mesa_swizzle_string(srcReg->Swizzle,
+ srcReg->Negate, GL_FALSE));
+#endif
+}
+
+
+static void
+fprint_comment(FILE *f, const struct prog_instruction *inst)
+{
+ if (inst->Comment)
+ fprintf(f, "; # %s\n", inst->Comment);
+ else
+ fprintf(f, ";\n");
+}
+
+
+void
+_mesa_fprint_alu_instruction(FILE *f,
+ const struct prog_instruction *inst,
+ const char *opcode_string, GLuint numRegs,
+ gl_prog_print_mode mode,
+ const struct gl_program *prog)
+{
+ GLuint j;
+
+ fprintf(f, "%s", opcode_string);
+ if (inst->CondUpdate)
+ fprintf(f, ".C");
+
+ /* frag prog only */
+ if (inst->SaturateMode == SATURATE_ZERO_ONE)
+ fprintf(f, "_SAT");
+
+ fprintf(f, " ");
+ if (inst->DstReg.File != PROGRAM_UNDEFINED) {
+ fprint_dst_reg(f, &inst->DstReg, mode, prog);
+ }
+ else {
+ fprintf(f, " ???");
+ }
+
+ if (numRegs > 0)
+ fprintf(f, ", ");
+
+ for (j = 0; j < numRegs; j++) {
+ fprint_src_reg(f, inst->SrcReg + j, mode, prog);
+ if (j + 1 < numRegs)
+ fprintf(f, ", ");
+ }
+
+ fprint_comment(f, inst);
+}
+
+
+void
+_mesa_print_alu_instruction(const struct prog_instruction *inst,
+ const char *opcode_string, GLuint numRegs)
+{
+ _mesa_fprint_alu_instruction(stderr, inst, opcode_string,
+ numRegs, PROG_PRINT_DEBUG, NULL);
+}
+
+
+/**
+ * Print a single vertex/fragment program instruction.
+ */
+GLint
+_mesa_fprint_instruction_opt(FILE *f,
+ const struct prog_instruction *inst,
+ GLint indent,
+ gl_prog_print_mode mode,
+ const struct gl_program *prog)
+{
+ GLint i;
+
+ if (inst->Opcode == OPCODE_ELSE ||
+ inst->Opcode == OPCODE_ENDIF ||
+ inst->Opcode == OPCODE_ENDLOOP ||
+ inst->Opcode == OPCODE_ENDSUB) {
+ indent -= 3;
+ }
+ for (i = 0; i < indent; i++) {
+ fprintf(f, " ");
+ }
+
+ switch (inst->Opcode) {
+ case OPCODE_PRINT:
+ fprintf(f, "PRINT '%s'", (char *) inst->Data);
+ if (inst->SrcReg[0].File != PROGRAM_UNDEFINED) {
+ fprintf(f, ", ");
+ fprintf(f, "%s[%d]%s",
+ _mesa_register_file_name((gl_register_file) inst->SrcReg[0].File),
+ inst->SrcReg[0].Index,
+ _mesa_swizzle_string(inst->SrcReg[0].Swizzle,
+ inst->SrcReg[0].Negate, GL_FALSE));
+ }
+ if (inst->Comment)
+ fprintf(f, " # %s", inst->Comment);
+ fprint_comment(f, inst);
+ break;
+ case OPCODE_SWZ:
+ fprintf(f, "SWZ");
+ if (inst->SaturateMode == SATURATE_ZERO_ONE)
+ fprintf(f, "_SAT");
+ fprintf(f, " ");
+ fprint_dst_reg(f, &inst->DstReg, mode, prog);
+ fprintf(f, ", %s[%d], %s",
+ _mesa_register_file_name((gl_register_file) inst->SrcReg[0].File),
+ inst->SrcReg[0].Index,
+ _mesa_swizzle_string(inst->SrcReg[0].Swizzle,
+ inst->SrcReg[0].Negate, GL_TRUE));
+ fprint_comment(f, inst);
+ break;
+ case OPCODE_TEX:
+ case OPCODE_TXP:
+ case OPCODE_TXL:
+ case OPCODE_TXB:
+ case OPCODE_TXD:
+ fprintf(f, "%s", _mesa_opcode_string(inst->Opcode));
+ if (inst->SaturateMode == SATURATE_ZERO_ONE)
+ fprintf(f, "_SAT");
+ fprintf(f, " ");
+ fprint_dst_reg(f, &inst->DstReg, mode, prog);
+ fprintf(f, ", ");
+ fprint_src_reg(f, &inst->SrcReg[0], mode, prog);
+ if (inst->Opcode == OPCODE_TXD) {
+ fprintf(f, ", ");
+ fprint_src_reg(f, &inst->SrcReg[1], mode, prog);
+ fprintf(f, ", ");
+ fprint_src_reg(f, &inst->SrcReg[2], mode, prog);
+ }
+ fprintf(f, ", texture[%d], ", inst->TexSrcUnit);
+ switch (inst->TexSrcTarget) {
+ case TEXTURE_1D_INDEX: fprintf(f, "1D"); break;
+ case TEXTURE_2D_INDEX: fprintf(f, "2D"); break;
+ case TEXTURE_3D_INDEX: fprintf(f, "3D"); break;
+ case TEXTURE_CUBE_INDEX: fprintf(f, "CUBE"); break;
+ case TEXTURE_RECT_INDEX: fprintf(f, "RECT"); break;
+ case TEXTURE_1D_ARRAY_INDEX: fprintf(f, "1D_ARRAY"); break;
+ case TEXTURE_2D_ARRAY_INDEX: fprintf(f, "2D_ARRAY"); break;
+ default:
+ ;
+ }
+ if (inst->TexShadow)
+ fprintf(f, " SHADOW");
+ fprint_comment(f, inst);
+ break;
+
+ case OPCODE_KIL:
+ fprintf(f, "%s", _mesa_opcode_string(inst->Opcode));
+ fprintf(f, " ");
+ fprint_src_reg(f, &inst->SrcReg[0], mode, prog);
+ fprint_comment(f, inst);
+ break;
+ case OPCODE_KIL_NV:
+ fprintf(f, "%s", _mesa_opcode_string(inst->Opcode));
+ fprintf(f, " ");
+ fprintf(f, "%s.%s",
+ _mesa_condcode_string(inst->DstReg.CondMask),
+ _mesa_swizzle_string(inst->DstReg.CondSwizzle,
+ GL_FALSE, GL_FALSE));
+ fprint_comment(f, inst);
+ break;
+
+ case OPCODE_ARL:
+ fprintf(f, "ARL ");
+ fprint_dst_reg(f, &inst->DstReg, mode, prog);
+ fprintf(f, ", ");
+ fprint_src_reg(f, &inst->SrcReg[0], mode, prog);
+ fprint_comment(f, inst);
+ break;
+ case OPCODE_BRA:
+ fprintf(f, "BRA %d (%s%s)",
+ inst->BranchTarget,
+ _mesa_condcode_string(inst->DstReg.CondMask),
+ _mesa_swizzle_string(inst->DstReg.CondSwizzle, 0, GL_FALSE));
+ fprint_comment(f, inst);
+ break;
+ case OPCODE_IF:
+ if (inst->SrcReg[0].File != PROGRAM_UNDEFINED) {
+ /* Use ordinary register */
+ fprintf(f, "IF ");
+ fprint_src_reg(f, &inst->SrcReg[0], mode, prog);
+ fprintf(f, "; ");
+ }
+ else {
+ /* Use cond codes */
+ fprintf(f, "IF (%s%s);",
+ _mesa_condcode_string(inst->DstReg.CondMask),
+ _mesa_swizzle_string(inst->DstReg.CondSwizzle,
+ 0, GL_FALSE));
+ }
+ fprintf(f, " # (if false, goto %d)", inst->BranchTarget);
+ fprint_comment(f, inst);
+ return indent + 3;
+ case OPCODE_ELSE:
+ fprintf(f, "ELSE; # (goto %d)\n", inst->BranchTarget);
+ return indent + 3;
+ case OPCODE_ENDIF:
+ fprintf(f, "ENDIF;\n");
+ break;
+ case OPCODE_BGNLOOP:
+ fprintf(f, "BGNLOOP; # (end at %d)\n", inst->BranchTarget);
+ return indent + 3;
+ case OPCODE_ENDLOOP:
+ fprintf(f, "ENDLOOP; # (goto %d)\n", inst->BranchTarget);
+ break;
+ case OPCODE_BRK:
+ case OPCODE_CONT:
+ fprintf(f, "%s (%s%s); # (goto %d)",
+ _mesa_opcode_string(inst->Opcode),
+ _mesa_condcode_string(inst->DstReg.CondMask),
+ _mesa_swizzle_string(inst->DstReg.CondSwizzle, 0, GL_FALSE),
+ inst->BranchTarget);
+ fprint_comment(f, inst);
+ break;
+
+ case OPCODE_BGNSUB:
+ if (mode == PROG_PRINT_NV) {
+ fprintf(f, "%s:\n", inst->Comment); /* comment is label */
+ return indent;
+ }
+ else {
+ fprintf(f, "BGNSUB");
+ fprint_comment(f, inst);
+ return indent + 3;
+ }
+ case OPCODE_ENDSUB:
+ if (mode == PROG_PRINT_DEBUG) {
+ fprintf(f, "ENDSUB");
+ fprint_comment(f, inst);
+ }
+ break;
+ case OPCODE_CAL:
+ if (mode == PROG_PRINT_NV) {
+ fprintf(f, "CAL %s; # (goto %d)\n", inst->Comment, inst->BranchTarget);
+ }
+ else {
+ fprintf(f, "CAL %u", inst->BranchTarget);
+ fprint_comment(f, inst);
+ }
+ break;
+ case OPCODE_RET:
+ fprintf(f, "RET (%s%s)",
+ _mesa_condcode_string(inst->DstReg.CondMask),
+ _mesa_swizzle_string(inst->DstReg.CondSwizzle, 0, GL_FALSE));
+ fprint_comment(f, inst);
+ break;
+
+ case OPCODE_END:
+ fprintf(f, "END\n");
+ break;
+ case OPCODE_NOP:
+ if (mode == PROG_PRINT_DEBUG) {
+ fprintf(f, "NOP");
+ fprint_comment(f, inst);
+ }
+ else if (inst->Comment) {
+ /* ARB/NV extensions don't have NOP instruction */
+ fprintf(f, "# %s\n", inst->Comment);
+ }
+ break;
+ case OPCODE_EMIT_VERTEX:
+ fprintf(f, "EMIT_VERTEX\n");
+ break;
+ case OPCODE_END_PRIMITIVE:
+ fprintf(f, "END_PRIMITIVE\n");
+ break;
+ /* XXX may need other special-case instructions */
+ default:
+ if (inst->Opcode < MAX_OPCODE) {
+ /* typical alu instruction */
+ _mesa_fprint_alu_instruction(f, inst,
+ _mesa_opcode_string(inst->Opcode),
+ _mesa_num_inst_src_regs(inst->Opcode),
+ mode, prog);
+ }
+ else {
+ _mesa_fprint_alu_instruction(f, inst,
+ _mesa_opcode_string(inst->Opcode),
+ 3/*_mesa_num_inst_src_regs(inst->Opcode)*/,
+ mode, prog);
+ }
+ break;
+ }
+ return indent;
+}
+
+
+GLint
+_mesa_print_instruction_opt(const struct prog_instruction *inst,
+ GLint indent,
+ gl_prog_print_mode mode,
+ const struct gl_program *prog)
+{
+ return _mesa_fprint_instruction_opt(stderr, inst, indent, mode, prog);
+}
+
+
+void
+_mesa_print_instruction(const struct prog_instruction *inst)
+{
+ /* note: 4th param should be ignored for PROG_PRINT_DEBUG */
+ _mesa_fprint_instruction_opt(stderr, inst, 0, PROG_PRINT_DEBUG, NULL);
+}
+
+
+
+/**
+ * Print program, with options.
+ */
+void
+_mesa_fprint_program_opt(FILE *f,
+ const struct gl_program *prog,
+ gl_prog_print_mode mode,
+ GLboolean lineNumbers)
+{
+ GLuint i, indent = 0;
+
+ switch (prog->Target) {
+ case GL_VERTEX_PROGRAM_ARB:
+ if (mode == PROG_PRINT_ARB)
+ fprintf(f, "!!ARBvp1.0\n");
+ else if (mode == PROG_PRINT_NV)
+ fprintf(f, "!!VP1.0\n");
+ else
+ fprintf(f, "# Vertex Program/Shader %u\n", prog->Id);
+ break;
+ case GL_FRAGMENT_PROGRAM_ARB:
+ case GL_FRAGMENT_PROGRAM_NV:
+ if (mode == PROG_PRINT_ARB)
+ fprintf(f, "!!ARBfp1.0\n");
+ else if (mode == PROG_PRINT_NV)
+ fprintf(f, "!!FP1.0\n");
+ else
+ fprintf(f, "# Fragment Program/Shader %u\n", prog->Id);
+ break;
+ case MESA_GEOMETRY_PROGRAM:
+ fprintf(f, "# Geometry Shader\n");
+ }
+
+ for (i = 0; i < prog->NumInstructions; i++) {
+ if (lineNumbers)
+ fprintf(f, "%3d: ", i);
+ indent = _mesa_fprint_instruction_opt(f, prog->Instructions + i,
+ indent, mode, prog);
+ }
+}
+
+
+/**
+ * Print program to stderr, default options.
+ */
+void
+_mesa_print_program(const struct gl_program *prog)
+{
+ _mesa_fprint_program_opt(stderr, prog, PROG_PRINT_DEBUG, GL_TRUE);
+}
+
+
+/**
+ * Return binary representation of 64-bit value (as a string).
+ * Insert a comma to separate each group of 8 bits.
+ * Note we return a pointer to local static storage so this is not
+ * re-entrant, etc.
+ * XXX move to imports.[ch] if useful elsewhere.
+ */
+static const char *
+binary(GLbitfield64 val)
+{
+ static char buf[80];
+ GLint i, len = 0;
+ for (i = 63; i >= 0; --i) {
+ if (val & (BITFIELD64_BIT(i)))
+ buf[len++] = '1';
+ else if (len > 0 || i == 0)
+ buf[len++] = '0';
+ if (len > 0 && ((i-1) % 8) == 7)
+ buf[len++] = ',';
+ }
+ buf[len] = '\0';
+ return buf;
+}
+
+
+/**
+ * Print all of a program's parameters/fields to given file.
+ */
+static void
+_mesa_fprint_program_parameters(FILE *f,
+ struct gl_context *ctx,
+ const struct gl_program *prog)
+{
+ GLuint i;
+
+ fprintf(f, "InputsRead: 0x%x (0b%s)\n",
+ prog->InputsRead, binary(prog->InputsRead));
+ fprintf(f, "OutputsWritten: 0x%llx (0b%s)\n",
+ (unsigned long long)prog->OutputsWritten,
+ binary(prog->OutputsWritten));
+ fprintf(f, "NumInstructions=%d\n", prog->NumInstructions);
+ fprintf(f, "NumTemporaries=%d\n", prog->NumTemporaries);
+ fprintf(f, "NumParameters=%d\n", prog->NumParameters);
+ fprintf(f, "NumAttributes=%d\n", prog->NumAttributes);
+ fprintf(f, "NumAddressRegs=%d\n", prog->NumAddressRegs);
+ fprintf(f, "IndirectRegisterFiles: 0x%x (0b%s)\n",
+ prog->IndirectRegisterFiles, binary(prog->IndirectRegisterFiles));
+ fprintf(f, "SamplersUsed: 0x%x (0b%s)\n",
+ prog->SamplersUsed, binary(prog->SamplersUsed));
+ fprintf(f, "Samplers=[ ");
+ for (i = 0; i < MAX_SAMPLERS; i++) {
+ fprintf(f, "%d ", prog->SamplerUnits[i]);
+ }
+ fprintf(f, "]\n");
+
+ _mesa_load_state_parameters(ctx, prog->Parameters);
+
+#if 0
+ fprintf(f, "Local Params:\n");
+ for (i = 0; i < MAX_PROGRAM_LOCAL_PARAMS; i++){
+ const GLfloat *p = prog->LocalParams[i];
+ fprintf(f, "%2d: %f, %f, %f, %f\n", i, p[0], p[1], p[2], p[3]);
+ }
+#endif
+ _mesa_print_parameter_list(prog->Parameters);
+}
+
+
+/**
+ * Print all of a program's parameters/fields to stderr.
+ */
+void
+_mesa_print_program_parameters(struct gl_context *ctx, const struct gl_program *prog)
+{
+ _mesa_fprint_program_parameters(stderr, ctx, prog);
+}
+
+
+/**
+ * Print a program parameter list to given file.
+ */
+static void
+_mesa_fprint_parameter_list(FILE *f,
+ const struct gl_program_parameter_list *list)
+{
+ GLuint i;
+
+ if (!list)
+ return;
+
+ if (0)
+ fprintf(f, "param list %p\n", (void *) list);
+ fprintf(f, "dirty state flags: 0x%x\n", list->StateFlags);
+ for (i = 0; i < list->NumParameters; i++){
+ struct gl_program_parameter *param = list->Parameters + i;
+ const GLfloat *v = list->ParameterValues[i];
+ fprintf(f, "param[%d] sz=%d %s %s = {%.3g, %.3g, %.3g, %.3g}",
+ i, param->Size,
+ _mesa_register_file_name(list->Parameters[i].Type),
+ param->Name, v[0], v[1], v[2], v[3]);
+ if (param->Flags & PROG_PARAM_BIT_CENTROID)
+ fprintf(f, " Centroid");
+ if (param->Flags & PROG_PARAM_BIT_INVARIANT)
+ fprintf(f, " Invariant");
+ if (param->Flags & PROG_PARAM_BIT_FLAT)
+ fprintf(f, " Flat");
+ if (param->Flags & PROG_PARAM_BIT_LINEAR)
+ fprintf(f, " Linear");
+ fprintf(f, "\n");
+ }
+}
+
+
+/**
+ * Print a program parameter list to stderr.
+ */
+void
+_mesa_print_parameter_list(const struct gl_program_parameter_list *list)
+{
+ _mesa_fprint_parameter_list(stderr, list);
+}
+
+
+/**
+ * Write shader and associated info to a file.
+ */
+void
+_mesa_write_shader_to_file(const struct gl_shader *shader)
+{
+ const char *type;
+ char filename[100];
+ FILE *f;
+
+ if (shader->Type == GL_FRAGMENT_SHADER)
+ type = "frag";
+ else if (shader->Type == GL_VERTEX_SHADER)
+ type = "vert";
+ else
+ type = "geom";
+
+ _mesa_snprintf(filename, sizeof(filename), "shader_%u.%s", shader->Name, type);
+ f = fopen(filename, "w");
+ if (!f) {
+ fprintf(stderr, "Unable to open %s for writing\n", filename);
+ return;
+ }
+
+ fprintf(f, "/* Shader %u source, checksum %u */\n", shader->Name, shader->SourceChecksum);
+ fputs(shader->Source, f);
+ fprintf(f, "\n");
+
+ fprintf(f, "/* Compile status: %s */\n",
+ shader->CompileStatus ? "ok" : "fail");
+ fprintf(f, "/* Log Info: */\n");
+ if (shader->InfoLog) {
+ fputs(shader->InfoLog, f);
+ }
+ if (shader->CompileStatus && shader->Program) {
+ fprintf(f, "/* GPU code */\n");
+ fprintf(f, "/*\n");
+ _mesa_fprint_program_opt(f, shader->Program, PROG_PRINT_DEBUG, GL_TRUE);
+ fprintf(f, "*/\n");
+ fprintf(f, "/* Parameters / constants */\n");
+ fprintf(f, "/*\n");
+ _mesa_fprint_parameter_list(f, shader->Program->Parameters);
+ fprintf(f, "*/\n");
+ }
+
+ fclose(f);
+}
+
+
+/**
+ * Append the shader's uniform info/values to the shader log file.
+ * The log file will typically have been created by the
+ * _mesa_write_shader_to_file function.
+ */
+void
+_mesa_append_uniforms_to_file(const struct gl_shader *shader,
+ const struct gl_program *prog)
+{
+ const char *type;
+ char filename[100];
+ FILE *f;
+
+ if (shader->Type == GL_FRAGMENT_SHADER)
+ type = "frag";
+ else
+ type = "vert";
+
+ _mesa_snprintf(filename, sizeof(filename), "shader_%u.%s", shader->Name, type);
+ f = fopen(filename, "a"); /* append */
+ if (!f) {
+ fprintf(stderr, "Unable to open %s for appending\n", filename);
+ return;
+ }
+
+ fprintf(f, "/* First-draw parameters / constants */\n");
+ fprintf(f, "/*\n");
+ _mesa_fprint_parameter_list(f, prog->Parameters);
+ fprintf(f, "*/\n");
+
+ fclose(f);
+}
diff --git a/mesalib/src/mesa/program/prog_statevars.c b/mesalib/src/mesa/program/prog_statevars.c index 16f9690e8..ddda78e19 100644 --- a/mesalib/src/mesa/program/prog_statevars.c +++ b/mesalib/src/mesa/program/prog_statevars.c @@ -1,1208 +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 <value>. Usually, four values are returned in <value> - * 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 * M_LOG2E); /* M_LOG2E == 1/ln(2) */ - 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->Sampler.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; - } else { - /* Flipping Y upside down (XY) followed by identity (ZW). */ - value[0] = -1.0F; - value[1] = (GLfloat) ctx->DrawBuffer->Height; - 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); - } - } -} +/*
+ * 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 <value>. Usually, four values are returned in <value>
+ * 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 * M_LOG2E); /* M_LOG2E == 1/ln(2) */
+ 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->Sampler.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;
+ } else {
+ /* Flipping Y upside down (XY) followed by identity (ZW). */
+ value[0] = -1.0F;
+ value[1] = (GLfloat) ctx->DrawBuffer->Height;
+ 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 04af3f4cf..07e0a2798 100644 --- a/mesalib/src/mesa/program/prog_statevars.h +++ b/mesalib/src/mesa/program/prog_statevars.h @@ -1,148 +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_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) if a FBO is bound, (-1, height, 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) if a FBO is bound, (-1, height, 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/program.c b/mesalib/src/mesa/program/program.c index 78efca9f1..c39f85e89 100644 --- a/mesalib/src/mesa/program/program.c +++ b/mesalib/src/mesa/program/program.c @@ -1,1076 +1,1076 @@ -/* - * 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 program.c - * Vertex and fragment program support functions. - * \author Brian Paul - */ - - -#include "main/glheader.h" -#include "main/context.h" -#include "main/hash.h" -#include "main/mfeatures.h" -#include "program.h" -#include "prog_cache.h" -#include "prog_parameter.h" -#include "prog_instruction.h" - - -/** - * A pointer to this dummy program is put into the hash table when - * glGenPrograms is called. - */ -struct gl_program _mesa_DummyProgram; - - -/** - * Init context's vertex/fragment program state - */ -void -_mesa_init_program(struct gl_context *ctx) -{ - GLuint i; - - /* - * If this assertion fails, we need to increase the field - * size for register indexes (see INST_INDEX_BITS). - */ - ASSERT(ctx->Const.VertexProgram.MaxUniformComponents / 4 - <= (1 << INST_INDEX_BITS)); - ASSERT(ctx->Const.FragmentProgram.MaxUniformComponents / 4 - <= (1 << INST_INDEX_BITS)); - - ASSERT(ctx->Const.VertexProgram.MaxTemps <= (1 << INST_INDEX_BITS)); - ASSERT(ctx->Const.VertexProgram.MaxLocalParams <= (1 << INST_INDEX_BITS)); - ASSERT(ctx->Const.FragmentProgram.MaxTemps <= (1 << INST_INDEX_BITS)); - ASSERT(ctx->Const.FragmentProgram.MaxLocalParams <= (1 << INST_INDEX_BITS)); - - ASSERT(ctx->Const.VertexProgram.MaxUniformComponents <= 4 * MAX_UNIFORMS); - ASSERT(ctx->Const.FragmentProgram.MaxUniformComponents <= 4 * MAX_UNIFORMS); - - ASSERT(ctx->Const.VertexProgram.MaxAddressOffset <= (1 << INST_INDEX_BITS)); - ASSERT(ctx->Const.FragmentProgram.MaxAddressOffset <= (1 << INST_INDEX_BITS)); - - /* If this fails, increase prog_instruction::TexSrcUnit size */ - ASSERT(MAX_TEXTURE_UNITS <= (1 << 5)); - - /* If this fails, increase prog_instruction::TexSrcTarget size */ - ASSERT(NUM_TEXTURE_TARGETS <= (1 << 3)); - - ctx->Program.ErrorPos = -1; - ctx->Program.ErrorString = _mesa_strdup(""); - -#if FEATURE_NV_vertex_program || FEATURE_ARB_vertex_program - ctx->VertexProgram.Enabled = GL_FALSE; -#if FEATURE_es2_glsl - ctx->VertexProgram.PointSizeEnabled = - (ctx->API == API_OPENGLES2) ? GL_TRUE : GL_FALSE; -#else - ctx->VertexProgram.PointSizeEnabled = GL_FALSE; -#endif - ctx->VertexProgram.TwoSideEnabled = GL_FALSE; - _mesa_reference_vertprog(ctx, &ctx->VertexProgram.Current, - ctx->Shared->DefaultVertexProgram); - assert(ctx->VertexProgram.Current); - for (i = 0; i < MAX_NV_VERTEX_PROGRAM_PARAMS / 4; i++) { - ctx->VertexProgram.TrackMatrix[i] = GL_NONE; - ctx->VertexProgram.TrackMatrixTransform[i] = GL_IDENTITY_NV; - } - ctx->VertexProgram.Cache = _mesa_new_program_cache(); -#endif - -#if FEATURE_NV_fragment_program || FEATURE_ARB_fragment_program - ctx->FragmentProgram.Enabled = GL_FALSE; - _mesa_reference_fragprog(ctx, &ctx->FragmentProgram.Current, - ctx->Shared->DefaultFragmentProgram); - assert(ctx->FragmentProgram.Current); - ctx->FragmentProgram.Cache = _mesa_new_program_cache(); -#endif - -#if FEATURE_ARB_geometry_shader4 - ctx->GeometryProgram.Enabled = GL_FALSE; - /* right now by default we don't have a geometry program */ - _mesa_reference_geomprog(ctx, &ctx->GeometryProgram.Current, - NULL); - ctx->GeometryProgram.Cache = _mesa_new_program_cache(); -#endif - - /* XXX probably move this stuff */ -#if FEATURE_ATI_fragment_shader - ctx->ATIFragmentShader.Enabled = GL_FALSE; - ctx->ATIFragmentShader.Current = ctx->Shared->DefaultFragmentShader; - assert(ctx->ATIFragmentShader.Current); - ctx->ATIFragmentShader.Current->RefCount++; -#endif -} - - -/** - * Free a context's vertex/fragment program state - */ -void -_mesa_free_program_data(struct gl_context *ctx) -{ -#if FEATURE_NV_vertex_program || FEATURE_ARB_vertex_program - _mesa_reference_vertprog(ctx, &ctx->VertexProgram.Current, NULL); - _mesa_delete_program_cache(ctx, ctx->VertexProgram.Cache); -#endif -#if FEATURE_NV_fragment_program || FEATURE_ARB_fragment_program - _mesa_reference_fragprog(ctx, &ctx->FragmentProgram.Current, NULL); - _mesa_delete_program_cache(ctx, ctx->FragmentProgram.Cache); -#endif -#if FEATURE_ARB_geometry_shader4 - _mesa_reference_geomprog(ctx, &ctx->GeometryProgram.Current, NULL); - _mesa_delete_program_cache(ctx, ctx->GeometryProgram.Cache); -#endif - /* XXX probably move this stuff */ -#if FEATURE_ATI_fragment_shader - if (ctx->ATIFragmentShader.Current) { - ctx->ATIFragmentShader.Current->RefCount--; - if (ctx->ATIFragmentShader.Current->RefCount <= 0) { - free(ctx->ATIFragmentShader.Current); - } - } -#endif - free((void *) ctx->Program.ErrorString); -} - - -/** - * Update the default program objects in the given context to reference those - * specified in the shared state and release those referencing the old - * shared state. - */ -void -_mesa_update_default_objects_program(struct gl_context *ctx) -{ -#if FEATURE_NV_vertex_program || FEATURE_ARB_vertex_program - _mesa_reference_vertprog(ctx, &ctx->VertexProgram.Current, - (struct gl_vertex_program *) - ctx->Shared->DefaultVertexProgram); - assert(ctx->VertexProgram.Current); -#endif - -#if FEATURE_NV_fragment_program || FEATURE_ARB_fragment_program - _mesa_reference_fragprog(ctx, &ctx->FragmentProgram.Current, - (struct gl_fragment_program *) - ctx->Shared->DefaultFragmentProgram); - assert(ctx->FragmentProgram.Current); -#endif - -#if FEATURE_ARB_geometry_shader4 - _mesa_reference_geomprog(ctx, &ctx->GeometryProgram.Current, - (struct gl_geometry_program *) - ctx->Shared->DefaultGeometryProgram); -#endif - - /* XXX probably move this stuff */ -#if FEATURE_ATI_fragment_shader - if (ctx->ATIFragmentShader.Current) { - ctx->ATIFragmentShader.Current->RefCount--; - if (ctx->ATIFragmentShader.Current->RefCount <= 0) { - free(ctx->ATIFragmentShader.Current); - } - } - ctx->ATIFragmentShader.Current = (struct ati_fragment_shader *) ctx->Shared->DefaultFragmentShader; - assert(ctx->ATIFragmentShader.Current); - ctx->ATIFragmentShader.Current->RefCount++; -#endif -} - - -/** - * Set the vertex/fragment program error state (position and error string). - * This is generally called from within the parsers. - */ -void -_mesa_set_program_error(struct gl_context *ctx, GLint pos, const char *string) -{ - ctx->Program.ErrorPos = pos; - free((void *) ctx->Program.ErrorString); - if (!string) - string = ""; - ctx->Program.ErrorString = _mesa_strdup(string); -} - - -/** - * Find the line number and column for 'pos' within 'string'. - * Return a copy of the line which contains 'pos'. Free the line with - * free(). - * \param string the program string - * \param pos the position within the string - * \param line returns the line number corresponding to 'pos'. - * \param col returns the column number corresponding to 'pos'. - * \return copy of the line containing 'pos'. - */ -const GLubyte * -_mesa_find_line_column(const GLubyte *string, const GLubyte *pos, - GLint *line, GLint *col) -{ - const GLubyte *lineStart = string; - const GLubyte *p = string; - GLubyte *s; - int len; - - *line = 1; - - while (p != pos) { - if (*p == (GLubyte) '\n') { - (*line)++; - lineStart = p + 1; - } - p++; - } - - *col = (pos - lineStart) + 1; - - /* return copy of this line */ - while (*p != 0 && *p != '\n') - p++; - len = p - lineStart; - s = (GLubyte *) malloc(len + 1); - memcpy(s, lineStart, len); - s[len] = 0; - - return s; -} - - -/** - * Initialize a new vertex/fragment program object. - */ -static struct gl_program * -_mesa_init_program_struct( struct gl_context *ctx, struct gl_program *prog, - GLenum target, GLuint id) -{ - (void) ctx; - if (prog) { - GLuint i; - memset(prog, 0, sizeof(*prog)); - prog->Id = id; - prog->Target = target; - prog->Resident = GL_TRUE; - prog->RefCount = 1; - prog->Format = GL_PROGRAM_FORMAT_ASCII_ARB; - - /* default mapping from samplers to texture units */ - for (i = 0; i < MAX_SAMPLERS; i++) - prog->SamplerUnits[i] = i; - } - - return prog; -} - - -/** - * Initialize a new fragment program object. - */ -struct gl_program * -_mesa_init_fragment_program( struct gl_context *ctx, struct gl_fragment_program *prog, - GLenum target, GLuint id) -{ - if (prog) - return _mesa_init_program_struct( ctx, &prog->Base, target, id ); - else - return NULL; -} - - -/** - * Initialize a new vertex program object. - */ -struct gl_program * -_mesa_init_vertex_program( struct gl_context *ctx, struct gl_vertex_program *prog, - GLenum target, GLuint id) -{ - if (prog) - return _mesa_init_program_struct( ctx, &prog->Base, target, id ); - else - return NULL; -} - - -/** - * Initialize a new geometry program object. - */ -struct gl_program * -_mesa_init_geometry_program( struct gl_context *ctx, struct gl_geometry_program *prog, - GLenum target, GLuint id) -{ - if (prog) - return _mesa_init_program_struct( ctx, &prog->Base, target, id ); - else - return NULL; -} - - -/** - * Allocate and initialize a new fragment/vertex program object but - * don't put it into the program hash table. Called via - * ctx->Driver.NewProgram. May be overridden (ie. replaced) by a - * device driver function to implement OO deriviation with additional - * types not understood by this function. - * - * \param ctx context - * \param id program id/number - * \param target program target/type - * \return pointer to new program object - */ -struct gl_program * -_mesa_new_program(struct gl_context *ctx, GLenum target, GLuint id) -{ - struct gl_program *prog; - switch (target) { - case GL_VERTEX_PROGRAM_ARB: /* == GL_VERTEX_PROGRAM_NV */ - case GL_VERTEX_STATE_PROGRAM_NV: - prog = _mesa_init_vertex_program(ctx, CALLOC_STRUCT(gl_vertex_program), - target, id ); - break; - case GL_FRAGMENT_PROGRAM_NV: - case GL_FRAGMENT_PROGRAM_ARB: - prog =_mesa_init_fragment_program(ctx, - CALLOC_STRUCT(gl_fragment_program), - target, id ); - break; - case MESA_GEOMETRY_PROGRAM: - prog = _mesa_init_geometry_program(ctx, - CALLOC_STRUCT(gl_geometry_program), - target, id); - break; - default: - _mesa_problem(ctx, "bad target in _mesa_new_program"); - prog = NULL; - } - return prog; -} - - -/** - * Delete a program and remove it from the hash table, ignoring the - * reference count. - * Called via ctx->Driver.DeleteProgram. May be wrapped (OO deriviation) - * by a device driver function. - */ -void -_mesa_delete_program(struct gl_context *ctx, struct gl_program *prog) -{ - (void) ctx; - ASSERT(prog); - ASSERT(prog->RefCount==0); - - if (prog == &_mesa_DummyProgram) - return; - - if (prog->String) - free(prog->String); - - _mesa_free_instructions(prog->Instructions, prog->NumInstructions); - - if (prog->Parameters) { - _mesa_free_parameter_list(prog->Parameters); - } - if (prog->Varying) { - _mesa_free_parameter_list(prog->Varying); - } - if (prog->Attributes) { - _mesa_free_parameter_list(prog->Attributes); - } - - free(prog); -} - - -/** - * Return the gl_program object for a given ID. - * Basically just a wrapper for _mesa_HashLookup() to avoid a lot of - * casts elsewhere. - */ -struct gl_program * -_mesa_lookup_program(struct gl_context *ctx, GLuint id) -{ - if (id) - return (struct gl_program *) _mesa_HashLookup(ctx->Shared->Programs, id); - else - return NULL; -} - - -/** - * Reference counting for vertex/fragment programs - */ -void -_mesa_reference_program(struct gl_context *ctx, - struct gl_program **ptr, - struct gl_program *prog) -{ - assert(ptr); - if (*ptr && prog) { - /* sanity check */ - if ((*ptr)->Target == GL_VERTEX_PROGRAM_ARB) - ASSERT(prog->Target == GL_VERTEX_PROGRAM_ARB); - else if ((*ptr)->Target == GL_FRAGMENT_PROGRAM_ARB) - ASSERT(prog->Target == GL_FRAGMENT_PROGRAM_ARB || - prog->Target == GL_FRAGMENT_PROGRAM_NV); - else if ((*ptr)->Target == MESA_GEOMETRY_PROGRAM) - ASSERT(prog->Target == MESA_GEOMETRY_PROGRAM); - } - if (*ptr == prog) { - return; /* no change */ - } - if (*ptr) { - GLboolean deleteFlag; - - /*_glthread_LOCK_MUTEX((*ptr)->Mutex);*/ -#if 0 - printf("Program %p ID=%u Target=%s Refcount-- to %d\n", - *ptr, (*ptr)->Id, - ((*ptr)->Target == GL_VERTEX_PROGRAM_ARB ? "VP" : - ((*ptr)->Target == MESA_GEOMETRY_PROGRAM ? "GP" : "FP")), - (*ptr)->RefCount - 1); -#endif - ASSERT((*ptr)->RefCount > 0); - (*ptr)->RefCount--; - - deleteFlag = ((*ptr)->RefCount == 0); - /*_glthread_UNLOCK_MUTEX((*ptr)->Mutex);*/ - - if (deleteFlag) { - ASSERT(ctx); - ctx->Driver.DeleteProgram(ctx, *ptr); - } - - *ptr = NULL; - } - - assert(!*ptr); - if (prog) { - /*_glthread_LOCK_MUTEX(prog->Mutex);*/ - prog->RefCount++; -#if 0 - printf("Program %p ID=%u Target=%s Refcount++ to %d\n", - prog, prog->Id, - (prog->Target == GL_VERTEX_PROGRAM_ARB ? "VP" : - (prog->Target == MESA_GEOMETRY_PROGRAM ? "GP" : "FP")), - prog->RefCount); -#endif - /*_glthread_UNLOCK_MUTEX(prog->Mutex);*/ - } - - *ptr = prog; -} - - -/** - * Return a copy of a program. - * XXX Problem here if the program object is actually OO-derivation - * made by a device driver. - */ -struct gl_program * -_mesa_clone_program(struct gl_context *ctx, const struct gl_program *prog) -{ - struct gl_program *clone; - - clone = ctx->Driver.NewProgram(ctx, prog->Target, prog->Id); - if (!clone) - return NULL; - - assert(clone->Target == prog->Target); - assert(clone->RefCount == 1); - - clone->String = (GLubyte *) _mesa_strdup((char *) prog->String); - clone->Format = prog->Format; - clone->Instructions = _mesa_alloc_instructions(prog->NumInstructions); - if (!clone->Instructions) { - _mesa_reference_program(ctx, &clone, NULL); - return NULL; - } - _mesa_copy_instructions(clone->Instructions, prog->Instructions, - prog->NumInstructions); - clone->InputsRead = prog->InputsRead; - clone->OutputsWritten = prog->OutputsWritten; - clone->SamplersUsed = prog->SamplersUsed; - clone->ShadowSamplers = prog->ShadowSamplers; - memcpy(clone->TexturesUsed, prog->TexturesUsed, sizeof(prog->TexturesUsed)); - - if (prog->Parameters) - clone->Parameters = _mesa_clone_parameter_list(prog->Parameters); - memcpy(clone->LocalParams, prog->LocalParams, sizeof(clone->LocalParams)); - if (prog->Varying) - clone->Varying = _mesa_clone_parameter_list(prog->Varying); - if (prog->Attributes) - clone->Attributes = _mesa_clone_parameter_list(prog->Attributes); - memcpy(clone->LocalParams, prog->LocalParams, sizeof(clone->LocalParams)); - clone->IndirectRegisterFiles = prog->IndirectRegisterFiles; - clone->NumInstructions = prog->NumInstructions; - clone->NumTemporaries = prog->NumTemporaries; - clone->NumParameters = prog->NumParameters; - clone->NumAttributes = prog->NumAttributes; - clone->NumAddressRegs = prog->NumAddressRegs; - clone->NumNativeInstructions = prog->NumNativeInstructions; - clone->NumNativeTemporaries = prog->NumNativeTemporaries; - clone->NumNativeParameters = prog->NumNativeParameters; - clone->NumNativeAttributes = prog->NumNativeAttributes; - clone->NumNativeAddressRegs = prog->NumNativeAddressRegs; - clone->NumAluInstructions = prog->NumAluInstructions; - clone->NumTexInstructions = prog->NumTexInstructions; - clone->NumTexIndirections = prog->NumTexIndirections; - clone->NumNativeAluInstructions = prog->NumNativeAluInstructions; - clone->NumNativeTexInstructions = prog->NumNativeTexInstructions; - clone->NumNativeTexIndirections = prog->NumNativeTexIndirections; - - switch (prog->Target) { - case GL_VERTEX_PROGRAM_ARB: - { - const struct gl_vertex_program *vp - = (const struct gl_vertex_program *) prog; - struct gl_vertex_program *vpc = (struct gl_vertex_program *) clone; - vpc->IsPositionInvariant = vp->IsPositionInvariant; - vpc->IsNVProgram = vp->IsNVProgram; - } - break; - case GL_FRAGMENT_PROGRAM_ARB: - { - const struct gl_fragment_program *fp - = (const struct gl_fragment_program *) prog; - struct gl_fragment_program *fpc = (struct gl_fragment_program *) clone; - fpc->UsesKill = fp->UsesKill; - fpc->OriginUpperLeft = fp->OriginUpperLeft; - fpc->PixelCenterInteger = fp->PixelCenterInteger; - } - break; - case MESA_GEOMETRY_PROGRAM: - { - const struct gl_geometry_program *gp - = (const struct gl_geometry_program *) prog; - struct gl_geometry_program *gpc = (struct gl_geometry_program *) clone; - gpc->VerticesOut = gp->VerticesOut; - gpc->InputType = gp->InputType; - gpc->OutputType = gp->OutputType; - } - break; - default: - _mesa_problem(NULL, "Unexpected target in _mesa_clone_program"); - } - - return clone; -} - - -/** - * Insert 'count' NOP instructions at 'start' in the given program. - * Adjust branch targets accordingly. - */ -GLboolean -_mesa_insert_instructions(struct gl_program *prog, GLuint start, GLuint count) -{ - const GLuint origLen = prog->NumInstructions; - const GLuint newLen = origLen + count; - struct prog_instruction *newInst; - GLuint i; - - /* adjust branches */ - for (i = 0; i < prog->NumInstructions; i++) { - struct prog_instruction *inst = prog->Instructions + i; - if (inst->BranchTarget > 0) { - if ((GLuint)inst->BranchTarget >= start) { - inst->BranchTarget += count; - } - } - } - - /* Alloc storage for new instructions */ - newInst = _mesa_alloc_instructions(newLen); - if (!newInst) { - return GL_FALSE; - } - - /* Copy 'start' instructions into new instruction buffer */ - _mesa_copy_instructions(newInst, prog->Instructions, start); - - /* init the new instructions */ - _mesa_init_instructions(newInst + start, count); - - /* Copy the remaining/tail instructions to new inst buffer */ - _mesa_copy_instructions(newInst + start + count, - prog->Instructions + start, - origLen - start); - - /* free old instructions */ - _mesa_free_instructions(prog->Instructions, origLen); - - /* install new instructions */ - prog->Instructions = newInst; - prog->NumInstructions = newLen; - - return GL_TRUE; -} - -/** - * Delete 'count' instructions at 'start' in the given program. - * Adjust branch targets accordingly. - */ -GLboolean -_mesa_delete_instructions(struct gl_program *prog, GLuint start, GLuint count) -{ - const GLuint origLen = prog->NumInstructions; - const GLuint newLen = origLen - count; - struct prog_instruction *newInst; - GLuint i; - - /* adjust branches */ - for (i = 0; i < prog->NumInstructions; i++) { - struct prog_instruction *inst = prog->Instructions + i; - if (inst->BranchTarget > 0) { - if (inst->BranchTarget > (GLint) start) { - inst->BranchTarget -= count; - } - } - } - - /* Alloc storage for new instructions */ - newInst = _mesa_alloc_instructions(newLen); - if (!newInst) { - return GL_FALSE; - } - - /* Copy 'start' instructions into new instruction buffer */ - _mesa_copy_instructions(newInst, prog->Instructions, start); - - /* Copy the remaining/tail instructions to new inst buffer */ - _mesa_copy_instructions(newInst + start, - prog->Instructions + start + count, - newLen - start); - - /* free old instructions */ - _mesa_free_instructions(prog->Instructions, origLen); - - /* install new instructions */ - prog->Instructions = newInst; - prog->NumInstructions = newLen; - - return GL_TRUE; -} - - -/** - * Search instructions for registers that match (oldFile, oldIndex), - * replacing them with (newFile, newIndex). - */ -static void -replace_registers(struct prog_instruction *inst, GLuint numInst, - GLuint oldFile, GLuint oldIndex, - GLuint newFile, GLuint newIndex) -{ - GLuint i, j; - for (i = 0; i < numInst; i++) { - /* src regs */ - for (j = 0; j < _mesa_num_inst_src_regs(inst[i].Opcode); j++) { - if (inst[i].SrcReg[j].File == oldFile && - inst[i].SrcReg[j].Index == oldIndex) { - inst[i].SrcReg[j].File = newFile; - inst[i].SrcReg[j].Index = newIndex; - } - } - /* dst reg */ - if (inst[i].DstReg.File == oldFile && inst[i].DstReg.Index == oldIndex) { - inst[i].DstReg.File = newFile; - inst[i].DstReg.Index = newIndex; - } - } -} - - -/** - * Search instructions for references to program parameters. When found, - * increment the parameter index by 'offset'. - * Used when combining programs. - */ -static void -adjust_param_indexes(struct prog_instruction *inst, GLuint numInst, - GLuint offset) -{ - GLuint i, j; - for (i = 0; i < numInst; i++) { - for (j = 0; j < _mesa_num_inst_src_regs(inst[i].Opcode); j++) { - GLuint f = inst[i].SrcReg[j].File; - if (f == PROGRAM_CONSTANT || - f == PROGRAM_UNIFORM || - f == PROGRAM_STATE_VAR) { - inst[i].SrcReg[j].Index += offset; - } - } - } -} - - -/** - * Combine two programs into one. Fix instructions so the outputs of - * the first program go to the inputs of the second program. - */ -struct gl_program * -_mesa_combine_programs(struct gl_context *ctx, - const struct gl_program *progA, - const struct gl_program *progB) -{ - struct prog_instruction *newInst; - struct gl_program *newProg; - const GLuint lenA = progA->NumInstructions - 1; /* omit END instr */ - const GLuint lenB = progB->NumInstructions; - const GLuint numParamsA = _mesa_num_parameters(progA->Parameters); - const GLuint newLength = lenA + lenB; - GLboolean usedTemps[MAX_PROGRAM_TEMPS]; - GLuint firstTemp = 0; - GLbitfield inputsB; - GLuint i; - - ASSERT(progA->Target == progB->Target); - - newInst = _mesa_alloc_instructions(newLength); - if (!newInst) - return GL_FALSE; - - _mesa_copy_instructions(newInst, progA->Instructions, lenA); - _mesa_copy_instructions(newInst + lenA, progB->Instructions, lenB); - - /* adjust branch / instruction addresses for B's instructions */ - for (i = 0; i < lenB; i++) { - newInst[lenA + i].BranchTarget += lenA; - } - - newProg = ctx->Driver.NewProgram(ctx, progA->Target, 0); - newProg->Instructions = newInst; - newProg->NumInstructions = newLength; - - /* find used temp regs (we may need new temps below) */ - _mesa_find_used_registers(newProg, PROGRAM_TEMPORARY, - usedTemps, MAX_PROGRAM_TEMPS); - - if (newProg->Target == GL_FRAGMENT_PROGRAM_ARB) { - struct gl_fragment_program *fprogA, *fprogB, *newFprog; - GLbitfield progB_inputsRead = progB->InputsRead; - GLint progB_colorFile, progB_colorIndex; - - fprogA = (struct gl_fragment_program *) progA; - fprogB = (struct gl_fragment_program *) progB; - newFprog = (struct gl_fragment_program *) newProg; - - newFprog->UsesKill = fprogA->UsesKill || fprogB->UsesKill; - - /* We'll do a search and replace for instances - * of progB_colorFile/progB_colorIndex below... - */ - progB_colorFile = PROGRAM_INPUT; - progB_colorIndex = FRAG_ATTRIB_COL0; - - /* - * The fragment program may get color from a state var rather than - * a fragment input (vertex output) if it's constant. - * See the texenvprogram.c code. - * So, search the program's parameter list now to see if the program - * gets color from a state var instead of a conventional fragment - * input register. - */ - for (i = 0; i < progB->Parameters->NumParameters; i++) { - struct gl_program_parameter *p = &progB->Parameters->Parameters[i]; - if (p->Type == PROGRAM_STATE_VAR && - p->StateIndexes[0] == STATE_INTERNAL && - p->StateIndexes[1] == STATE_CURRENT_ATTRIB && - (int) p->StateIndexes[2] == (int) VERT_ATTRIB_COLOR0) { - progB_inputsRead |= FRAG_BIT_COL0; - progB_colorFile = PROGRAM_STATE_VAR; - progB_colorIndex = i; - break; - } - } - - /* Connect color outputs of fprogA to color inputs of fprogB, via a - * new temporary register. - */ - if ((progA->OutputsWritten & BITFIELD64_BIT(FRAG_RESULT_COLOR)) && - (progB_inputsRead & FRAG_BIT_COL0)) { - GLint tempReg = _mesa_find_free_register(usedTemps, MAX_PROGRAM_TEMPS, - firstTemp); - if (tempReg < 0) { - _mesa_problem(ctx, "No free temp regs found in " - "_mesa_combine_programs(), using 31"); - tempReg = 31; - } - firstTemp = tempReg + 1; - - /* replace writes to result.color[0] with tempReg */ - replace_registers(newInst, lenA, - PROGRAM_OUTPUT, FRAG_RESULT_COLOR, - PROGRAM_TEMPORARY, tempReg); - /* replace reads from the input color with tempReg */ - replace_registers(newInst + lenA, lenB, - progB_colorFile, progB_colorIndex, /* search for */ - PROGRAM_TEMPORARY, tempReg /* replace with */ ); - } - - /* compute combined program's InputsRead */ - inputsB = progB_inputsRead; - if (progA->OutputsWritten & BITFIELD64_BIT(FRAG_RESULT_COLOR)) { - inputsB &= ~(1 << FRAG_ATTRIB_COL0); - } - newProg->InputsRead = progA->InputsRead | inputsB; - newProg->OutputsWritten = progB->OutputsWritten; - newProg->SamplersUsed = progA->SamplersUsed | progB->SamplersUsed; - } - else { - /* vertex program */ - assert(0); /* XXX todo */ - } - - /* - * Merge parameters (uniforms, constants, etc) - */ - newProg->Parameters = _mesa_combine_parameter_lists(progA->Parameters, - progB->Parameters); - - adjust_param_indexes(newInst + lenA, lenB, numParamsA); - - - return newProg; -} - - -/** - * Populate the 'used' array with flags indicating which registers (TEMPs, - * INPUTs, OUTPUTs, etc, are used by the given program. - * \param file type of register to scan for - * \param used returns true/false flags for in use / free - * \param usedSize size of the 'used' array - */ -void -_mesa_find_used_registers(const struct gl_program *prog, - gl_register_file file, - GLboolean used[], GLuint usedSize) -{ - GLuint i, j; - - memset(used, 0, usedSize); - - for (i = 0; i < prog->NumInstructions; i++) { - const struct prog_instruction *inst = prog->Instructions + i; - const GLuint n = _mesa_num_inst_src_regs(inst->Opcode); - - if (inst->DstReg.File == file) { - ASSERT(inst->DstReg.Index < usedSize); - if(inst->DstReg.Index < usedSize) - used[inst->DstReg.Index] = GL_TRUE; - } - - for (j = 0; j < n; j++) { - if (inst->SrcReg[j].File == file) { - ASSERT(inst->SrcReg[j].Index < usedSize); - if(inst->SrcReg[j].Index < usedSize) - used[inst->SrcReg[j].Index] = GL_TRUE; - } - } - } -} - - -/** - * Scan the given 'used' register flag array for the first entry - * that's >= firstReg. - * \param used vector of flags indicating registers in use (as returned - * by _mesa_find_used_registers()) - * \param usedSize size of the 'used' array - * \param firstReg first register to start searching at - * \return index of unused register, or -1 if none. - */ -GLint -_mesa_find_free_register(const GLboolean used[], - GLuint usedSize, GLuint firstReg) -{ - GLuint i; - - assert(firstReg < usedSize); - - for (i = firstReg; i < usedSize; i++) - if (!used[i]) - return i; - - return -1; -} - - - -/** - * Check if the given register index is valid (doesn't exceed implementation- - * dependent limits). - * \return GL_TRUE if OK, GL_FALSE if bad index - */ -GLboolean -_mesa_valid_register_index(const struct gl_context *ctx, - gl_shader_type shaderType, - gl_register_file file, GLint index) -{ - const struct gl_program_constants *c; - - switch (shaderType) { - case MESA_SHADER_VERTEX: - c = &ctx->Const.VertexProgram; - break; - case MESA_SHADER_FRAGMENT: - c = &ctx->Const.FragmentProgram; - break; - case MESA_SHADER_GEOMETRY: - c = &ctx->Const.GeometryProgram; - break; - default: - _mesa_problem(ctx, - "unexpected shader type in _mesa_valid_register_index()"); - return GL_FALSE; - } - - switch (file) { - case PROGRAM_UNDEFINED: - return GL_TRUE; /* XXX or maybe false? */ - - case PROGRAM_TEMPORARY: - return index >= 0 && index < c->MaxTemps; - - case PROGRAM_ENV_PARAM: - return index >= 0 && index < c->MaxEnvParams; - - case PROGRAM_LOCAL_PARAM: - return index >= 0 && index < c->MaxLocalParams; - - case PROGRAM_NAMED_PARAM: - return index >= 0 && index < c->MaxParameters; - - case PROGRAM_UNIFORM: - case PROGRAM_STATE_VAR: - /* aka constant buffer */ - return index >= 0 && index < c->MaxUniformComponents / 4; - - case PROGRAM_CONSTANT: - /* constant buffer w/ possible relative negative addressing */ - return (index > (int) c->MaxUniformComponents / -4 && - index < c->MaxUniformComponents / 4); - - case PROGRAM_INPUT: - if (index < 0) - return GL_FALSE; - - switch (shaderType) { - case MESA_SHADER_VERTEX: - return index < VERT_ATTRIB_GENERIC0 + c->MaxAttribs; - case MESA_SHADER_FRAGMENT: - return index < FRAG_ATTRIB_VAR0 + ctx->Const.MaxVarying; - case MESA_SHADER_GEOMETRY: - return index < GEOM_ATTRIB_VAR0 + ctx->Const.MaxVarying; - default: - return GL_FALSE; - } - - case PROGRAM_OUTPUT: - if (index < 0) - return GL_FALSE; - - switch (shaderType) { - case MESA_SHADER_VERTEX: - return index < VERT_RESULT_VAR0 + ctx->Const.MaxVarying; - case MESA_SHADER_FRAGMENT: - return index < FRAG_RESULT_DATA0 + ctx->Const.MaxDrawBuffers; - case MESA_SHADER_GEOMETRY: - return index < GEOM_RESULT_VAR0 + ctx->Const.MaxVarying; - default: - return GL_FALSE; - } - - case PROGRAM_ADDRESS: - return index >= 0 && index < c->MaxAddressRegs; - - default: - _mesa_problem(ctx, - "unexpected register file in _mesa_valid_register_index()"); - return GL_FALSE; - } -} - - - -/** - * "Post-process" a GPU program. This is intended to be used for debugging. - * Example actions include no-op'ing instructions or changing instruction - * behaviour. - */ -void -_mesa_postprocess_program(struct gl_context *ctx, struct gl_program *prog) -{ - static const GLfloat white[4] = { 0.5, 0.5, 0.5, 0.5 }; - GLuint i; - GLuint whiteSwizzle; - GLint whiteIndex = _mesa_add_unnamed_constant(prog->Parameters, - white, 4, &whiteSwizzle); - - (void) whiteIndex; - - for (i = 0; i < prog->NumInstructions; i++) { - struct prog_instruction *inst = prog->Instructions + i; - const GLuint n = _mesa_num_inst_src_regs(inst->Opcode); - - (void) n; - - if (_mesa_is_tex_instruction(inst->Opcode)) { -#if 0 - /* replace TEX/TXP/TXB with MOV */ - inst->Opcode = OPCODE_MOV; - inst->DstReg.WriteMask = WRITEMASK_XYZW; - inst->SrcReg[0].Swizzle = SWIZZLE_XYZW; - inst->SrcReg[0].Negate = NEGATE_NONE; -#endif - -#if 0 - /* disable shadow texture mode */ - inst->TexShadow = 0; -#endif - } - - if (inst->Opcode == OPCODE_TXP) { -#if 0 - inst->Opcode = OPCODE_MOV; - inst->DstReg.WriteMask = WRITEMASK_XYZW; - inst->SrcReg[0].File = PROGRAM_CONSTANT; - inst->SrcReg[0].Index = whiteIndex; - inst->SrcReg[0].Swizzle = SWIZZLE_XYZW; - inst->SrcReg[0].Negate = NEGATE_NONE; -#endif -#if 0 - inst->TexShadow = 0; -#endif -#if 0 - inst->Opcode = OPCODE_TEX; - inst->TexShadow = 0; -#endif - } - - } -} +/*
+ * 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 program.c
+ * Vertex and fragment program support functions.
+ * \author Brian Paul
+ */
+
+
+#include "main/glheader.h"
+#include "main/context.h"
+#include "main/hash.h"
+#include "main/mfeatures.h"
+#include "program.h"
+#include "prog_cache.h"
+#include "prog_parameter.h"
+#include "prog_instruction.h"
+
+
+/**
+ * A pointer to this dummy program is put into the hash table when
+ * glGenPrograms is called.
+ */
+struct gl_program _mesa_DummyProgram;
+
+
+/**
+ * Init context's vertex/fragment program state
+ */
+void
+_mesa_init_program(struct gl_context *ctx)
+{
+ GLuint i;
+
+ /*
+ * If this assertion fails, we need to increase the field
+ * size for register indexes (see INST_INDEX_BITS).
+ */
+ ASSERT(ctx->Const.VertexProgram.MaxUniformComponents / 4
+ <= (1 << INST_INDEX_BITS));
+ ASSERT(ctx->Const.FragmentProgram.MaxUniformComponents / 4
+ <= (1 << INST_INDEX_BITS));
+
+ ASSERT(ctx->Const.VertexProgram.MaxTemps <= (1 << INST_INDEX_BITS));
+ ASSERT(ctx->Const.VertexProgram.MaxLocalParams <= (1 << INST_INDEX_BITS));
+ ASSERT(ctx->Const.FragmentProgram.MaxTemps <= (1 << INST_INDEX_BITS));
+ ASSERT(ctx->Const.FragmentProgram.MaxLocalParams <= (1 << INST_INDEX_BITS));
+
+ ASSERT(ctx->Const.VertexProgram.MaxUniformComponents <= 4 * MAX_UNIFORMS);
+ ASSERT(ctx->Const.FragmentProgram.MaxUniformComponents <= 4 * MAX_UNIFORMS);
+
+ ASSERT(ctx->Const.VertexProgram.MaxAddressOffset <= (1 << INST_INDEX_BITS));
+ ASSERT(ctx->Const.FragmentProgram.MaxAddressOffset <= (1 << INST_INDEX_BITS));
+
+ /* If this fails, increase prog_instruction::TexSrcUnit size */
+ ASSERT(MAX_TEXTURE_UNITS <= (1 << 5));
+
+ /* If this fails, increase prog_instruction::TexSrcTarget size */
+ ASSERT(NUM_TEXTURE_TARGETS <= (1 << 3));
+
+ ctx->Program.ErrorPos = -1;
+ ctx->Program.ErrorString = _mesa_strdup("");
+
+#if FEATURE_NV_vertex_program || FEATURE_ARB_vertex_program
+ ctx->VertexProgram.Enabled = GL_FALSE;
+#if FEATURE_es2_glsl
+ ctx->VertexProgram.PointSizeEnabled =
+ (ctx->API == API_OPENGLES2) ? GL_TRUE : GL_FALSE;
+#else
+ ctx->VertexProgram.PointSizeEnabled = GL_FALSE;
+#endif
+ ctx->VertexProgram.TwoSideEnabled = GL_FALSE;
+ _mesa_reference_vertprog(ctx, &ctx->VertexProgram.Current,
+ ctx->Shared->DefaultVertexProgram);
+ assert(ctx->VertexProgram.Current);
+ for (i = 0; i < MAX_NV_VERTEX_PROGRAM_PARAMS / 4; i++) {
+ ctx->VertexProgram.TrackMatrix[i] = GL_NONE;
+ ctx->VertexProgram.TrackMatrixTransform[i] = GL_IDENTITY_NV;
+ }
+ ctx->VertexProgram.Cache = _mesa_new_program_cache();
+#endif
+
+#if FEATURE_NV_fragment_program || FEATURE_ARB_fragment_program
+ ctx->FragmentProgram.Enabled = GL_FALSE;
+ _mesa_reference_fragprog(ctx, &ctx->FragmentProgram.Current,
+ ctx->Shared->DefaultFragmentProgram);
+ assert(ctx->FragmentProgram.Current);
+ ctx->FragmentProgram.Cache = _mesa_new_program_cache();
+#endif
+
+#if FEATURE_ARB_geometry_shader4
+ ctx->GeometryProgram.Enabled = GL_FALSE;
+ /* right now by default we don't have a geometry program */
+ _mesa_reference_geomprog(ctx, &ctx->GeometryProgram.Current,
+ NULL);
+ ctx->GeometryProgram.Cache = _mesa_new_program_cache();
+#endif
+
+ /* XXX probably move this stuff */
+#if FEATURE_ATI_fragment_shader
+ ctx->ATIFragmentShader.Enabled = GL_FALSE;
+ ctx->ATIFragmentShader.Current = ctx->Shared->DefaultFragmentShader;
+ assert(ctx->ATIFragmentShader.Current);
+ ctx->ATIFragmentShader.Current->RefCount++;
+#endif
+}
+
+
+/**
+ * Free a context's vertex/fragment program state
+ */
+void
+_mesa_free_program_data(struct gl_context *ctx)
+{
+#if FEATURE_NV_vertex_program || FEATURE_ARB_vertex_program
+ _mesa_reference_vertprog(ctx, &ctx->VertexProgram.Current, NULL);
+ _mesa_delete_program_cache(ctx, ctx->VertexProgram.Cache);
+#endif
+#if FEATURE_NV_fragment_program || FEATURE_ARB_fragment_program
+ _mesa_reference_fragprog(ctx, &ctx->FragmentProgram.Current, NULL);
+ _mesa_delete_program_cache(ctx, ctx->FragmentProgram.Cache);
+#endif
+#if FEATURE_ARB_geometry_shader4
+ _mesa_reference_geomprog(ctx, &ctx->GeometryProgram.Current, NULL);
+ _mesa_delete_program_cache(ctx, ctx->GeometryProgram.Cache);
+#endif
+ /* XXX probably move this stuff */
+#if FEATURE_ATI_fragment_shader
+ if (ctx->ATIFragmentShader.Current) {
+ ctx->ATIFragmentShader.Current->RefCount--;
+ if (ctx->ATIFragmentShader.Current->RefCount <= 0) {
+ free(ctx->ATIFragmentShader.Current);
+ }
+ }
+#endif
+ free((void *) ctx->Program.ErrorString);
+}
+
+
+/**
+ * Update the default program objects in the given context to reference those
+ * specified in the shared state and release those referencing the old
+ * shared state.
+ */
+void
+_mesa_update_default_objects_program(struct gl_context *ctx)
+{
+#if FEATURE_NV_vertex_program || FEATURE_ARB_vertex_program
+ _mesa_reference_vertprog(ctx, &ctx->VertexProgram.Current,
+ (struct gl_vertex_program *)
+ ctx->Shared->DefaultVertexProgram);
+ assert(ctx->VertexProgram.Current);
+#endif
+
+#if FEATURE_NV_fragment_program || FEATURE_ARB_fragment_program
+ _mesa_reference_fragprog(ctx, &ctx->FragmentProgram.Current,
+ (struct gl_fragment_program *)
+ ctx->Shared->DefaultFragmentProgram);
+ assert(ctx->FragmentProgram.Current);
+#endif
+
+#if FEATURE_ARB_geometry_shader4
+ _mesa_reference_geomprog(ctx, &ctx->GeometryProgram.Current,
+ (struct gl_geometry_program *)
+ ctx->Shared->DefaultGeometryProgram);
+#endif
+
+ /* XXX probably move this stuff */
+#if FEATURE_ATI_fragment_shader
+ if (ctx->ATIFragmentShader.Current) {
+ ctx->ATIFragmentShader.Current->RefCount--;
+ if (ctx->ATIFragmentShader.Current->RefCount <= 0) {
+ free(ctx->ATIFragmentShader.Current);
+ }
+ }
+ ctx->ATIFragmentShader.Current = (struct ati_fragment_shader *) ctx->Shared->DefaultFragmentShader;
+ assert(ctx->ATIFragmentShader.Current);
+ ctx->ATIFragmentShader.Current->RefCount++;
+#endif
+}
+
+
+/**
+ * Set the vertex/fragment program error state (position and error string).
+ * This is generally called from within the parsers.
+ */
+void
+_mesa_set_program_error(struct gl_context *ctx, GLint pos, const char *string)
+{
+ ctx->Program.ErrorPos = pos;
+ free((void *) ctx->Program.ErrorString);
+ if (!string)
+ string = "";
+ ctx->Program.ErrorString = _mesa_strdup(string);
+}
+
+
+/**
+ * Find the line number and column for 'pos' within 'string'.
+ * Return a copy of the line which contains 'pos'. Free the line with
+ * free().
+ * \param string the program string
+ * \param pos the position within the string
+ * \param line returns the line number corresponding to 'pos'.
+ * \param col returns the column number corresponding to 'pos'.
+ * \return copy of the line containing 'pos'.
+ */
+const GLubyte *
+_mesa_find_line_column(const GLubyte *string, const GLubyte *pos,
+ GLint *line, GLint *col)
+{
+ const GLubyte *lineStart = string;
+ const GLubyte *p = string;
+ GLubyte *s;
+ int len;
+
+ *line = 1;
+
+ while (p != pos) {
+ if (*p == (GLubyte) '\n') {
+ (*line)++;
+ lineStart = p + 1;
+ }
+ p++;
+ }
+
+ *col = (pos - lineStart) + 1;
+
+ /* return copy of this line */
+ while (*p != 0 && *p != '\n')
+ p++;
+ len = p - lineStart;
+ s = (GLubyte *) malloc(len + 1);
+ memcpy(s, lineStart, len);
+ s[len] = 0;
+
+ return s;
+}
+
+
+/**
+ * Initialize a new vertex/fragment program object.
+ */
+static struct gl_program *
+_mesa_init_program_struct( struct gl_context *ctx, struct gl_program *prog,
+ GLenum target, GLuint id)
+{
+ (void) ctx;
+ if (prog) {
+ GLuint i;
+ memset(prog, 0, sizeof(*prog));
+ prog->Id = id;
+ prog->Target = target;
+ prog->Resident = GL_TRUE;
+ prog->RefCount = 1;
+ prog->Format = GL_PROGRAM_FORMAT_ASCII_ARB;
+
+ /* default mapping from samplers to texture units */
+ for (i = 0; i < MAX_SAMPLERS; i++)
+ prog->SamplerUnits[i] = i;
+ }
+
+ return prog;
+}
+
+
+/**
+ * Initialize a new fragment program object.
+ */
+struct gl_program *
+_mesa_init_fragment_program( struct gl_context *ctx, struct gl_fragment_program *prog,
+ GLenum target, GLuint id)
+{
+ if (prog)
+ return _mesa_init_program_struct( ctx, &prog->Base, target, id );
+ else
+ return NULL;
+}
+
+
+/**
+ * Initialize a new vertex program object.
+ */
+struct gl_program *
+_mesa_init_vertex_program( struct gl_context *ctx, struct gl_vertex_program *prog,
+ GLenum target, GLuint id)
+{
+ if (prog)
+ return _mesa_init_program_struct( ctx, &prog->Base, target, id );
+ else
+ return NULL;
+}
+
+
+/**
+ * Initialize a new geometry program object.
+ */
+struct gl_program *
+_mesa_init_geometry_program( struct gl_context *ctx, struct gl_geometry_program *prog,
+ GLenum target, GLuint id)
+{
+ if (prog)
+ return _mesa_init_program_struct( ctx, &prog->Base, target, id );
+ else
+ return NULL;
+}
+
+
+/**
+ * Allocate and initialize a new fragment/vertex program object but
+ * don't put it into the program hash table. Called via
+ * ctx->Driver.NewProgram. May be overridden (ie. replaced) by a
+ * device driver function to implement OO deriviation with additional
+ * types not understood by this function.
+ *
+ * \param ctx context
+ * \param id program id/number
+ * \param target program target/type
+ * \return pointer to new program object
+ */
+struct gl_program *
+_mesa_new_program(struct gl_context *ctx, GLenum target, GLuint id)
+{
+ struct gl_program *prog;
+ switch (target) {
+ case GL_VERTEX_PROGRAM_ARB: /* == GL_VERTEX_PROGRAM_NV */
+ case GL_VERTEX_STATE_PROGRAM_NV:
+ prog = _mesa_init_vertex_program(ctx, CALLOC_STRUCT(gl_vertex_program),
+ target, id );
+ break;
+ case GL_FRAGMENT_PROGRAM_NV:
+ case GL_FRAGMENT_PROGRAM_ARB:
+ prog =_mesa_init_fragment_program(ctx,
+ CALLOC_STRUCT(gl_fragment_program),
+ target, id );
+ break;
+ case MESA_GEOMETRY_PROGRAM:
+ prog = _mesa_init_geometry_program(ctx,
+ CALLOC_STRUCT(gl_geometry_program),
+ target, id);
+ break;
+ default:
+ _mesa_problem(ctx, "bad target in _mesa_new_program");
+ prog = NULL;
+ }
+ return prog;
+}
+
+
+/**
+ * Delete a program and remove it from the hash table, ignoring the
+ * reference count.
+ * Called via ctx->Driver.DeleteProgram. May be wrapped (OO deriviation)
+ * by a device driver function.
+ */
+void
+_mesa_delete_program(struct gl_context *ctx, struct gl_program *prog)
+{
+ (void) ctx;
+ ASSERT(prog);
+ ASSERT(prog->RefCount==0);
+
+ if (prog == &_mesa_DummyProgram)
+ return;
+
+ if (prog->String)
+ free(prog->String);
+
+ _mesa_free_instructions(prog->Instructions, prog->NumInstructions);
+
+ if (prog->Parameters) {
+ _mesa_free_parameter_list(prog->Parameters);
+ }
+ if (prog->Varying) {
+ _mesa_free_parameter_list(prog->Varying);
+ }
+ if (prog->Attributes) {
+ _mesa_free_parameter_list(prog->Attributes);
+ }
+
+ free(prog);
+}
+
+
+/**
+ * Return the gl_program object for a given ID.
+ * Basically just a wrapper for _mesa_HashLookup() to avoid a lot of
+ * casts elsewhere.
+ */
+struct gl_program *
+_mesa_lookup_program(struct gl_context *ctx, GLuint id)
+{
+ if (id)
+ return (struct gl_program *) _mesa_HashLookup(ctx->Shared->Programs, id);
+ else
+ return NULL;
+}
+
+
+/**
+ * Reference counting for vertex/fragment programs
+ */
+void
+_mesa_reference_program(struct gl_context *ctx,
+ struct gl_program **ptr,
+ struct gl_program *prog)
+{
+ assert(ptr);
+ if (*ptr && prog) {
+ /* sanity check */
+ if ((*ptr)->Target == GL_VERTEX_PROGRAM_ARB)
+ ASSERT(prog->Target == GL_VERTEX_PROGRAM_ARB);
+ else if ((*ptr)->Target == GL_FRAGMENT_PROGRAM_ARB)
+ ASSERT(prog->Target == GL_FRAGMENT_PROGRAM_ARB ||
+ prog->Target == GL_FRAGMENT_PROGRAM_NV);
+ else if ((*ptr)->Target == MESA_GEOMETRY_PROGRAM)
+ ASSERT(prog->Target == MESA_GEOMETRY_PROGRAM);
+ }
+ if (*ptr == prog) {
+ return; /* no change */
+ }
+ if (*ptr) {
+ GLboolean deleteFlag;
+
+ /*_glthread_LOCK_MUTEX((*ptr)->Mutex);*/
+#if 0
+ printf("Program %p ID=%u Target=%s Refcount-- to %d\n",
+ *ptr, (*ptr)->Id,
+ ((*ptr)->Target == GL_VERTEX_PROGRAM_ARB ? "VP" :
+ ((*ptr)->Target == MESA_GEOMETRY_PROGRAM ? "GP" : "FP")),
+ (*ptr)->RefCount - 1);
+#endif
+ ASSERT((*ptr)->RefCount > 0);
+ (*ptr)->RefCount--;
+
+ deleteFlag = ((*ptr)->RefCount == 0);
+ /*_glthread_UNLOCK_MUTEX((*ptr)->Mutex);*/
+
+ if (deleteFlag) {
+ ASSERT(ctx);
+ ctx->Driver.DeleteProgram(ctx, *ptr);
+ }
+
+ *ptr = NULL;
+ }
+
+ assert(!*ptr);
+ if (prog) {
+ /*_glthread_LOCK_MUTEX(prog->Mutex);*/
+ prog->RefCount++;
+#if 0
+ printf("Program %p ID=%u Target=%s Refcount++ to %d\n",
+ prog, prog->Id,
+ (prog->Target == GL_VERTEX_PROGRAM_ARB ? "VP" :
+ (prog->Target == MESA_GEOMETRY_PROGRAM ? "GP" : "FP")),
+ prog->RefCount);
+#endif
+ /*_glthread_UNLOCK_MUTEX(prog->Mutex);*/
+ }
+
+ *ptr = prog;
+}
+
+
+/**
+ * Return a copy of a program.
+ * XXX Problem here if the program object is actually OO-derivation
+ * made by a device driver.
+ */
+struct gl_program *
+_mesa_clone_program(struct gl_context *ctx, const struct gl_program *prog)
+{
+ struct gl_program *clone;
+
+ clone = ctx->Driver.NewProgram(ctx, prog->Target, prog->Id);
+ if (!clone)
+ return NULL;
+
+ assert(clone->Target == prog->Target);
+ assert(clone->RefCount == 1);
+
+ clone->String = (GLubyte *) _mesa_strdup((char *) prog->String);
+ clone->Format = prog->Format;
+ clone->Instructions = _mesa_alloc_instructions(prog->NumInstructions);
+ if (!clone->Instructions) {
+ _mesa_reference_program(ctx, &clone, NULL);
+ return NULL;
+ }
+ _mesa_copy_instructions(clone->Instructions, prog->Instructions,
+ prog->NumInstructions);
+ clone->InputsRead = prog->InputsRead;
+ clone->OutputsWritten = prog->OutputsWritten;
+ clone->SamplersUsed = prog->SamplersUsed;
+ clone->ShadowSamplers = prog->ShadowSamplers;
+ memcpy(clone->TexturesUsed, prog->TexturesUsed, sizeof(prog->TexturesUsed));
+
+ if (prog->Parameters)
+ clone->Parameters = _mesa_clone_parameter_list(prog->Parameters);
+ memcpy(clone->LocalParams, prog->LocalParams, sizeof(clone->LocalParams));
+ if (prog->Varying)
+ clone->Varying = _mesa_clone_parameter_list(prog->Varying);
+ if (prog->Attributes)
+ clone->Attributes = _mesa_clone_parameter_list(prog->Attributes);
+ memcpy(clone->LocalParams, prog->LocalParams, sizeof(clone->LocalParams));
+ clone->IndirectRegisterFiles = prog->IndirectRegisterFiles;
+ clone->NumInstructions = prog->NumInstructions;
+ clone->NumTemporaries = prog->NumTemporaries;
+ clone->NumParameters = prog->NumParameters;
+ clone->NumAttributes = prog->NumAttributes;
+ clone->NumAddressRegs = prog->NumAddressRegs;
+ clone->NumNativeInstructions = prog->NumNativeInstructions;
+ clone->NumNativeTemporaries = prog->NumNativeTemporaries;
+ clone->NumNativeParameters = prog->NumNativeParameters;
+ clone->NumNativeAttributes = prog->NumNativeAttributes;
+ clone->NumNativeAddressRegs = prog->NumNativeAddressRegs;
+ clone->NumAluInstructions = prog->NumAluInstructions;
+ clone->NumTexInstructions = prog->NumTexInstructions;
+ clone->NumTexIndirections = prog->NumTexIndirections;
+ clone->NumNativeAluInstructions = prog->NumNativeAluInstructions;
+ clone->NumNativeTexInstructions = prog->NumNativeTexInstructions;
+ clone->NumNativeTexIndirections = prog->NumNativeTexIndirections;
+
+ switch (prog->Target) {
+ case GL_VERTEX_PROGRAM_ARB:
+ {
+ const struct gl_vertex_program *vp
+ = (const struct gl_vertex_program *) prog;
+ struct gl_vertex_program *vpc = (struct gl_vertex_program *) clone;
+ vpc->IsPositionInvariant = vp->IsPositionInvariant;
+ vpc->IsNVProgram = vp->IsNVProgram;
+ }
+ break;
+ case GL_FRAGMENT_PROGRAM_ARB:
+ {
+ const struct gl_fragment_program *fp
+ = (const struct gl_fragment_program *) prog;
+ struct gl_fragment_program *fpc = (struct gl_fragment_program *) clone;
+ fpc->UsesKill = fp->UsesKill;
+ fpc->OriginUpperLeft = fp->OriginUpperLeft;
+ fpc->PixelCenterInteger = fp->PixelCenterInteger;
+ }
+ break;
+ case MESA_GEOMETRY_PROGRAM:
+ {
+ const struct gl_geometry_program *gp
+ = (const struct gl_geometry_program *) prog;
+ struct gl_geometry_program *gpc = (struct gl_geometry_program *) clone;
+ gpc->VerticesOut = gp->VerticesOut;
+ gpc->InputType = gp->InputType;
+ gpc->OutputType = gp->OutputType;
+ }
+ break;
+ default:
+ _mesa_problem(NULL, "Unexpected target in _mesa_clone_program");
+ }
+
+ return clone;
+}
+
+
+/**
+ * Insert 'count' NOP instructions at 'start' in the given program.
+ * Adjust branch targets accordingly.
+ */
+GLboolean
+_mesa_insert_instructions(struct gl_program *prog, GLuint start, GLuint count)
+{
+ const GLuint origLen = prog->NumInstructions;
+ const GLuint newLen = origLen + count;
+ struct prog_instruction *newInst;
+ GLuint i;
+
+ /* adjust branches */
+ for (i = 0; i < prog->NumInstructions; i++) {
+ struct prog_instruction *inst = prog->Instructions + i;
+ if (inst->BranchTarget > 0) {
+ if ((GLuint)inst->BranchTarget >= start) {
+ inst->BranchTarget += count;
+ }
+ }
+ }
+
+ /* Alloc storage for new instructions */
+ newInst = _mesa_alloc_instructions(newLen);
+ if (!newInst) {
+ return GL_FALSE;
+ }
+
+ /* Copy 'start' instructions into new instruction buffer */
+ _mesa_copy_instructions(newInst, prog->Instructions, start);
+
+ /* init the new instructions */
+ _mesa_init_instructions(newInst + start, count);
+
+ /* Copy the remaining/tail instructions to new inst buffer */
+ _mesa_copy_instructions(newInst + start + count,
+ prog->Instructions + start,
+ origLen - start);
+
+ /* free old instructions */
+ _mesa_free_instructions(prog->Instructions, origLen);
+
+ /* install new instructions */
+ prog->Instructions = newInst;
+ prog->NumInstructions = newLen;
+
+ return GL_TRUE;
+}
+
+/**
+ * Delete 'count' instructions at 'start' in the given program.
+ * Adjust branch targets accordingly.
+ */
+GLboolean
+_mesa_delete_instructions(struct gl_program *prog, GLuint start, GLuint count)
+{
+ const GLuint origLen = prog->NumInstructions;
+ const GLuint newLen = origLen - count;
+ struct prog_instruction *newInst;
+ GLuint i;
+
+ /* adjust branches */
+ for (i = 0; i < prog->NumInstructions; i++) {
+ struct prog_instruction *inst = prog->Instructions + i;
+ if (inst->BranchTarget > 0) {
+ if (inst->BranchTarget > (GLint) start) {
+ inst->BranchTarget -= count;
+ }
+ }
+ }
+
+ /* Alloc storage for new instructions */
+ newInst = _mesa_alloc_instructions(newLen);
+ if (!newInst) {
+ return GL_FALSE;
+ }
+
+ /* Copy 'start' instructions into new instruction buffer */
+ _mesa_copy_instructions(newInst, prog->Instructions, start);
+
+ /* Copy the remaining/tail instructions to new inst buffer */
+ _mesa_copy_instructions(newInst + start,
+ prog->Instructions + start + count,
+ newLen - start);
+
+ /* free old instructions */
+ _mesa_free_instructions(prog->Instructions, origLen);
+
+ /* install new instructions */
+ prog->Instructions = newInst;
+ prog->NumInstructions = newLen;
+
+ return GL_TRUE;
+}
+
+
+/**
+ * Search instructions for registers that match (oldFile, oldIndex),
+ * replacing them with (newFile, newIndex).
+ */
+static void
+replace_registers(struct prog_instruction *inst, GLuint numInst,
+ GLuint oldFile, GLuint oldIndex,
+ GLuint newFile, GLuint newIndex)
+{
+ GLuint i, j;
+ for (i = 0; i < numInst; i++) {
+ /* src regs */
+ for (j = 0; j < _mesa_num_inst_src_regs(inst[i].Opcode); j++) {
+ if (inst[i].SrcReg[j].File == oldFile &&
+ inst[i].SrcReg[j].Index == oldIndex) {
+ inst[i].SrcReg[j].File = newFile;
+ inst[i].SrcReg[j].Index = newIndex;
+ }
+ }
+ /* dst reg */
+ if (inst[i].DstReg.File == oldFile && inst[i].DstReg.Index == oldIndex) {
+ inst[i].DstReg.File = newFile;
+ inst[i].DstReg.Index = newIndex;
+ }
+ }
+}
+
+
+/**
+ * Search instructions for references to program parameters. When found,
+ * increment the parameter index by 'offset'.
+ * Used when combining programs.
+ */
+static void
+adjust_param_indexes(struct prog_instruction *inst, GLuint numInst,
+ GLuint offset)
+{
+ GLuint i, j;
+ for (i = 0; i < numInst; i++) {
+ for (j = 0; j < _mesa_num_inst_src_regs(inst[i].Opcode); j++) {
+ GLuint f = inst[i].SrcReg[j].File;
+ if (f == PROGRAM_CONSTANT ||
+ f == PROGRAM_UNIFORM ||
+ f == PROGRAM_STATE_VAR) {
+ inst[i].SrcReg[j].Index += offset;
+ }
+ }
+ }
+}
+
+
+/**
+ * Combine two programs into one. Fix instructions so the outputs of
+ * the first program go to the inputs of the second program.
+ */
+struct gl_program *
+_mesa_combine_programs(struct gl_context *ctx,
+ const struct gl_program *progA,
+ const struct gl_program *progB)
+{
+ struct prog_instruction *newInst;
+ struct gl_program *newProg;
+ const GLuint lenA = progA->NumInstructions - 1; /* omit END instr */
+ const GLuint lenB = progB->NumInstructions;
+ const GLuint numParamsA = _mesa_num_parameters(progA->Parameters);
+ const GLuint newLength = lenA + lenB;
+ GLboolean usedTemps[MAX_PROGRAM_TEMPS];
+ GLuint firstTemp = 0;
+ GLbitfield inputsB;
+ GLuint i;
+
+ ASSERT(progA->Target == progB->Target);
+
+ newInst = _mesa_alloc_instructions(newLength);
+ if (!newInst)
+ return GL_FALSE;
+
+ _mesa_copy_instructions(newInst, progA->Instructions, lenA);
+ _mesa_copy_instructions(newInst + lenA, progB->Instructions, lenB);
+
+ /* adjust branch / instruction addresses for B's instructions */
+ for (i = 0; i < lenB; i++) {
+ newInst[lenA + i].BranchTarget += lenA;
+ }
+
+ newProg = ctx->Driver.NewProgram(ctx, progA->Target, 0);
+ newProg->Instructions = newInst;
+ newProg->NumInstructions = newLength;
+
+ /* find used temp regs (we may need new temps below) */
+ _mesa_find_used_registers(newProg, PROGRAM_TEMPORARY,
+ usedTemps, MAX_PROGRAM_TEMPS);
+
+ if (newProg->Target == GL_FRAGMENT_PROGRAM_ARB) {
+ struct gl_fragment_program *fprogA, *fprogB, *newFprog;
+ GLbitfield progB_inputsRead = progB->InputsRead;
+ GLint progB_colorFile, progB_colorIndex;
+
+ fprogA = (struct gl_fragment_program *) progA;
+ fprogB = (struct gl_fragment_program *) progB;
+ newFprog = (struct gl_fragment_program *) newProg;
+
+ newFprog->UsesKill = fprogA->UsesKill || fprogB->UsesKill;
+
+ /* We'll do a search and replace for instances
+ * of progB_colorFile/progB_colorIndex below...
+ */
+ progB_colorFile = PROGRAM_INPUT;
+ progB_colorIndex = FRAG_ATTRIB_COL0;
+
+ /*
+ * The fragment program may get color from a state var rather than
+ * a fragment input (vertex output) if it's constant.
+ * See the texenvprogram.c code.
+ * So, search the program's parameter list now to see if the program
+ * gets color from a state var instead of a conventional fragment
+ * input register.
+ */
+ for (i = 0; i < progB->Parameters->NumParameters; i++) {
+ struct gl_program_parameter *p = &progB->Parameters->Parameters[i];
+ if (p->Type == PROGRAM_STATE_VAR &&
+ p->StateIndexes[0] == STATE_INTERNAL &&
+ p->StateIndexes[1] == STATE_CURRENT_ATTRIB &&
+ (int) p->StateIndexes[2] == (int) VERT_ATTRIB_COLOR0) {
+ progB_inputsRead |= FRAG_BIT_COL0;
+ progB_colorFile = PROGRAM_STATE_VAR;
+ progB_colorIndex = i;
+ break;
+ }
+ }
+
+ /* Connect color outputs of fprogA to color inputs of fprogB, via a
+ * new temporary register.
+ */
+ if ((progA->OutputsWritten & BITFIELD64_BIT(FRAG_RESULT_COLOR)) &&
+ (progB_inputsRead & FRAG_BIT_COL0)) {
+ GLint tempReg = _mesa_find_free_register(usedTemps, MAX_PROGRAM_TEMPS,
+ firstTemp);
+ if (tempReg < 0) {
+ _mesa_problem(ctx, "No free temp regs found in "
+ "_mesa_combine_programs(), using 31");
+ tempReg = 31;
+ }
+ firstTemp = tempReg + 1;
+
+ /* replace writes to result.color[0] with tempReg */
+ replace_registers(newInst, lenA,
+ PROGRAM_OUTPUT, FRAG_RESULT_COLOR,
+ PROGRAM_TEMPORARY, tempReg);
+ /* replace reads from the input color with tempReg */
+ replace_registers(newInst + lenA, lenB,
+ progB_colorFile, progB_colorIndex, /* search for */
+ PROGRAM_TEMPORARY, tempReg /* replace with */ );
+ }
+
+ /* compute combined program's InputsRead */
+ inputsB = progB_inputsRead;
+ if (progA->OutputsWritten & BITFIELD64_BIT(FRAG_RESULT_COLOR)) {
+ inputsB &= ~(1 << FRAG_ATTRIB_COL0);
+ }
+ newProg->InputsRead = progA->InputsRead | inputsB;
+ newProg->OutputsWritten = progB->OutputsWritten;
+ newProg->SamplersUsed = progA->SamplersUsed | progB->SamplersUsed;
+ }
+ else {
+ /* vertex program */
+ assert(0); /* XXX todo */
+ }
+
+ /*
+ * Merge parameters (uniforms, constants, etc)
+ */
+ newProg->Parameters = _mesa_combine_parameter_lists(progA->Parameters,
+ progB->Parameters);
+
+ adjust_param_indexes(newInst + lenA, lenB, numParamsA);
+
+
+ return newProg;
+}
+
+
+/**
+ * Populate the 'used' array with flags indicating which registers (TEMPs,
+ * INPUTs, OUTPUTs, etc, are used by the given program.
+ * \param file type of register to scan for
+ * \param used returns true/false flags for in use / free
+ * \param usedSize size of the 'used' array
+ */
+void
+_mesa_find_used_registers(const struct gl_program *prog,
+ gl_register_file file,
+ GLboolean used[], GLuint usedSize)
+{
+ GLuint i, j;
+
+ memset(used, 0, usedSize);
+
+ for (i = 0; i < prog->NumInstructions; i++) {
+ const struct prog_instruction *inst = prog->Instructions + i;
+ const GLuint n = _mesa_num_inst_src_regs(inst->Opcode);
+
+ if (inst->DstReg.File == file) {
+ ASSERT(inst->DstReg.Index < usedSize);
+ if(inst->DstReg.Index < usedSize)
+ used[inst->DstReg.Index] = GL_TRUE;
+ }
+
+ for (j = 0; j < n; j++) {
+ if (inst->SrcReg[j].File == file) {
+ ASSERT(inst->SrcReg[j].Index < usedSize);
+ if(inst->SrcReg[j].Index < usedSize)
+ used[inst->SrcReg[j].Index] = GL_TRUE;
+ }
+ }
+ }
+}
+
+
+/**
+ * Scan the given 'used' register flag array for the first entry
+ * that's >= firstReg.
+ * \param used vector of flags indicating registers in use (as returned
+ * by _mesa_find_used_registers())
+ * \param usedSize size of the 'used' array
+ * \param firstReg first register to start searching at
+ * \return index of unused register, or -1 if none.
+ */
+GLint
+_mesa_find_free_register(const GLboolean used[],
+ GLuint usedSize, GLuint firstReg)
+{
+ GLuint i;
+
+ assert(firstReg < usedSize);
+
+ for (i = firstReg; i < usedSize; i++)
+ if (!used[i])
+ return i;
+
+ return -1;
+}
+
+
+
+/**
+ * Check if the given register index is valid (doesn't exceed implementation-
+ * dependent limits).
+ * \return GL_TRUE if OK, GL_FALSE if bad index
+ */
+GLboolean
+_mesa_valid_register_index(const struct gl_context *ctx,
+ gl_shader_type shaderType,
+ gl_register_file file, GLint index)
+{
+ const struct gl_program_constants *c;
+
+ switch (shaderType) {
+ case MESA_SHADER_VERTEX:
+ c = &ctx->Const.VertexProgram;
+ break;
+ case MESA_SHADER_FRAGMENT:
+ c = &ctx->Const.FragmentProgram;
+ break;
+ case MESA_SHADER_GEOMETRY:
+ c = &ctx->Const.GeometryProgram;
+ break;
+ default:
+ _mesa_problem(ctx,
+ "unexpected shader type in _mesa_valid_register_index()");
+ return GL_FALSE;
+ }
+
+ switch (file) {
+ case PROGRAM_UNDEFINED:
+ return GL_TRUE; /* XXX or maybe false? */
+
+ case PROGRAM_TEMPORARY:
+ return index >= 0 && index < c->MaxTemps;
+
+ case PROGRAM_ENV_PARAM:
+ return index >= 0 && index < c->MaxEnvParams;
+
+ case PROGRAM_LOCAL_PARAM:
+ return index >= 0 && index < c->MaxLocalParams;
+
+ case PROGRAM_NAMED_PARAM:
+ return index >= 0 && index < c->MaxParameters;
+
+ case PROGRAM_UNIFORM:
+ case PROGRAM_STATE_VAR:
+ /* aka constant buffer */
+ return index >= 0 && index < c->MaxUniformComponents / 4;
+
+ case PROGRAM_CONSTANT:
+ /* constant buffer w/ possible relative negative addressing */
+ return (index > (int) c->MaxUniformComponents / -4 &&
+ index < c->MaxUniformComponents / 4);
+
+ case PROGRAM_INPUT:
+ if (index < 0)
+ return GL_FALSE;
+
+ switch (shaderType) {
+ case MESA_SHADER_VERTEX:
+ return index < VERT_ATTRIB_GENERIC0 + c->MaxAttribs;
+ case MESA_SHADER_FRAGMENT:
+ return index < FRAG_ATTRIB_VAR0 + ctx->Const.MaxVarying;
+ case MESA_SHADER_GEOMETRY:
+ return index < GEOM_ATTRIB_VAR0 + ctx->Const.MaxVarying;
+ default:
+ return GL_FALSE;
+ }
+
+ case PROGRAM_OUTPUT:
+ if (index < 0)
+ return GL_FALSE;
+
+ switch (shaderType) {
+ case MESA_SHADER_VERTEX:
+ return index < VERT_RESULT_VAR0 + ctx->Const.MaxVarying;
+ case MESA_SHADER_FRAGMENT:
+ return index < FRAG_RESULT_DATA0 + ctx->Const.MaxDrawBuffers;
+ case MESA_SHADER_GEOMETRY:
+ return index < GEOM_RESULT_VAR0 + ctx->Const.MaxVarying;
+ default:
+ return GL_FALSE;
+ }
+
+ case PROGRAM_ADDRESS:
+ return index >= 0 && index < c->MaxAddressRegs;
+
+ default:
+ _mesa_problem(ctx,
+ "unexpected register file in _mesa_valid_register_index()");
+ return GL_FALSE;
+ }
+}
+
+
+
+/**
+ * "Post-process" a GPU program. This is intended to be used for debugging.
+ * Example actions include no-op'ing instructions or changing instruction
+ * behaviour.
+ */
+void
+_mesa_postprocess_program(struct gl_context *ctx, struct gl_program *prog)
+{
+ static const GLfloat white[4] = { 0.5, 0.5, 0.5, 0.5 };
+ GLuint i;
+ GLuint whiteSwizzle;
+ GLint whiteIndex = _mesa_add_unnamed_constant(prog->Parameters,
+ white, 4, &whiteSwizzle);
+
+ (void) whiteIndex;
+
+ for (i = 0; i < prog->NumInstructions; i++) {
+ struct prog_instruction *inst = prog->Instructions + i;
+ const GLuint n = _mesa_num_inst_src_regs(inst->Opcode);
+
+ (void) n;
+
+ if (_mesa_is_tex_instruction(inst->Opcode)) {
+#if 0
+ /* replace TEX/TXP/TXB with MOV */
+ inst->Opcode = OPCODE_MOV;
+ inst->DstReg.WriteMask = WRITEMASK_XYZW;
+ inst->SrcReg[0].Swizzle = SWIZZLE_XYZW;
+ inst->SrcReg[0].Negate = NEGATE_NONE;
+#endif
+
+#if 0
+ /* disable shadow texture mode */
+ inst->TexShadow = 0;
+#endif
+ }
+
+ if (inst->Opcode == OPCODE_TXP) {
+#if 0
+ inst->Opcode = OPCODE_MOV;
+ inst->DstReg.WriteMask = WRITEMASK_XYZW;
+ inst->SrcReg[0].File = PROGRAM_CONSTANT;
+ inst->SrcReg[0].Index = whiteIndex;
+ inst->SrcReg[0].Swizzle = SWIZZLE_XYZW;
+ inst->SrcReg[0].Negate = NEGATE_NONE;
+#endif
+#if 0
+ inst->TexShadow = 0;
+#endif
+#if 0
+ inst->Opcode = OPCODE_TEX;
+ inst->TexShadow = 0;
+#endif
+ }
+
+ }
+}
diff --git a/mesalib/src/mesa/program/program_parse.y b/mesalib/src/mesa/program/program_parse.y index dbf5abaa6..e7752fcbd 100644 --- a/mesalib/src/mesa/program/program_parse.y +++ b/mesalib/src/mesa/program/program_parse.y @@ -1,2806 +1,2806 @@ -%{ -/* - * Copyright © 2009 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. - */ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "main/mtypes.h" -#include "main/imports.h" -#include "program/program.h" -#include "program/prog_parameter.h" -#include "program/prog_parameter_layout.h" -#include "program/prog_statevars.h" -#include "program/prog_instruction.h" - -#include "program/symbol_table.h" -#include "program/program_parser.h" - -extern void *yy_scan_string(char *); -extern void yy_delete_buffer(void *); - -static struct asm_symbol *declare_variable(struct asm_parser_state *state, - char *name, enum asm_type t, struct YYLTYPE *locp); - -static int add_state_reference(struct gl_program_parameter_list *param_list, - const gl_state_index tokens[STATE_LENGTH]); - -static int initialize_symbol_from_state(struct gl_program *prog, - struct asm_symbol *param_var, const gl_state_index tokens[STATE_LENGTH]); - -static int initialize_symbol_from_param(struct gl_program *prog, - struct asm_symbol *param_var, const gl_state_index tokens[STATE_LENGTH]); - -static int initialize_symbol_from_const(struct gl_program *prog, - struct asm_symbol *param_var, const struct asm_vector *vec, - GLboolean allowSwizzle); - -static int yyparse(struct asm_parser_state *state); - -static char *make_error_string(const char *fmt, ...); - -static void yyerror(struct YYLTYPE *locp, struct asm_parser_state *state, - const char *s); - -static int validate_inputs(struct YYLTYPE *locp, - struct asm_parser_state *state); - -static void init_dst_reg(struct prog_dst_register *r); - -static void set_dst_reg(struct prog_dst_register *r, - gl_register_file file, GLint index); - -static void init_src_reg(struct asm_src_register *r); - -static void set_src_reg(struct asm_src_register *r, - gl_register_file file, GLint index); - -static void set_src_reg_swz(struct asm_src_register *r, - gl_register_file file, GLint index, GLuint swizzle); - -static void asm_instruction_set_operands(struct asm_instruction *inst, - const struct prog_dst_register *dst, const struct asm_src_register *src0, - const struct asm_src_register *src1, const struct asm_src_register *src2); - -static struct asm_instruction *asm_instruction_ctor(gl_inst_opcode op, - const struct prog_dst_register *dst, const struct asm_src_register *src0, - const struct asm_src_register *src1, const struct asm_src_register *src2); - -static struct asm_instruction *asm_instruction_copy_ctor( - const struct prog_instruction *base, const struct prog_dst_register *dst, - const struct asm_src_register *src0, const struct asm_src_register *src1, - const struct asm_src_register *src2); - -#ifndef FALSE -#define FALSE 0 -#define TRUE (!FALSE) -#endif - -#define YYLLOC_DEFAULT(Current, Rhs, N) \ - do { \ - if (YYID(N)) { \ - (Current).first_line = YYRHSLOC(Rhs, 1).first_line; \ - (Current).first_column = YYRHSLOC(Rhs, 1).first_column; \ - (Current).position = YYRHSLOC(Rhs, 1).position; \ - (Current).last_line = YYRHSLOC(Rhs, N).last_line; \ - (Current).last_column = YYRHSLOC(Rhs, N).last_column; \ - } else { \ - (Current).first_line = YYRHSLOC(Rhs, 0).last_line; \ - (Current).last_line = (Current).first_line; \ - (Current).first_column = YYRHSLOC(Rhs, 0).last_column; \ - (Current).last_column = (Current).first_column; \ - (Current).position = YYRHSLOC(Rhs, 0).position \ - + (Current).first_column; \ - } \ - } while(YYID(0)) - -#define YYLEX_PARAM state->scanner -%} - -%pure-parser -%locations -%parse-param { struct asm_parser_state *state } -%error-verbose -%lex-param { void *scanner } - -%union { - struct asm_instruction *inst; - struct asm_symbol *sym; - struct asm_symbol temp_sym; - struct asm_swizzle_mask swiz_mask; - struct asm_src_register src_reg; - struct prog_dst_register dst_reg; - struct prog_instruction temp_inst; - char *string; - unsigned result; - unsigned attrib; - int integer; - float real; - gl_state_index state[STATE_LENGTH]; - int negate; - struct asm_vector vector; - gl_inst_opcode opcode; - - struct { - unsigned swz; - unsigned rgba_valid:1; - unsigned xyzw_valid:1; - unsigned negate:1; - } ext_swizzle; -} - -%token ARBvp_10 ARBfp_10 - -/* Tokens for assembler pseudo-ops */ -%token <integer> ADDRESS -%token ALIAS ATTRIB -%token OPTION OUTPUT -%token PARAM -%token <integer> TEMP -%token END - - /* Tokens for instructions */ -%token <temp_inst> BIN_OP BINSC_OP SAMPLE_OP SCALAR_OP TRI_OP VECTOR_OP -%token <temp_inst> ARL KIL SWZ TXD_OP - -%token <integer> INTEGER -%token <real> REAL - -%token AMBIENT ATTENUATION -%token BACK -%token CLIP COLOR -%token DEPTH DIFFUSE DIRECTION -%token EMISSION ENV EYE -%token FOG FOGCOORD FRAGMENT FRONT -%token HALF -%token INVERSE INVTRANS -%token LIGHT LIGHTMODEL LIGHTPROD LOCAL -%token MATERIAL MAT_PROGRAM MATRIX MATRIXINDEX MODELVIEW MVP -%token NORMAL -%token OBJECT -%token PALETTE PARAMS PLANE POINT_TOK POINTSIZE POSITION PRIMARY PROGRAM PROJECTION -%token RANGE RESULT ROW -%token SCENECOLOR SECONDARY SHININESS SIZE_TOK SPECULAR SPOT STATE -%token TEXCOORD TEXENV TEXGEN TEXGEN_Q TEXGEN_R TEXGEN_S TEXGEN_T TEXTURE TRANSPOSE -%token TEXTURE_UNIT TEX_1D TEX_2D TEX_3D TEX_CUBE TEX_RECT -%token TEX_SHADOW1D TEX_SHADOW2D TEX_SHADOWRECT -%token TEX_ARRAY1D TEX_ARRAY2D TEX_ARRAYSHADOW1D TEX_ARRAYSHADOW2D -%token VERTEX VTXATTRIB -%token WEIGHT - -%token <string> IDENTIFIER USED_IDENTIFIER -%type <string> string -%token <swiz_mask> MASK4 MASK3 MASK2 MASK1 SWIZZLE -%token DOT_DOT -%token DOT - -%type <inst> instruction ALU_instruction TexInstruction -%type <inst> ARL_instruction VECTORop_instruction -%type <inst> SCALARop_instruction BINSCop_instruction BINop_instruction -%type <inst> TRIop_instruction TXD_instruction SWZ_instruction SAMPLE_instruction -%type <inst> KIL_instruction - -%type <dst_reg> dstReg maskedDstReg maskedAddrReg -%type <src_reg> srcReg scalarUse scalarSrcReg swizzleSrcReg -%type <swiz_mask> scalarSuffix swizzleSuffix extendedSwizzle -%type <ext_swizzle> extSwizComp extSwizSel -%type <swiz_mask> optionalMask - -%type <sym> progParamArray -%type <integer> addrRegRelOffset addrRegPosOffset addrRegNegOffset -%type <src_reg> progParamArrayMem progParamArrayAbs progParamArrayRel -%type <sym> addrReg -%type <swiz_mask> addrComponent addrWriteMask - -%type <dst_reg> ccMaskRule ccTest ccMaskRule2 ccTest2 optionalCcMask - -%type <result> resultBinding resultColBinding -%type <integer> optFaceType optColorType -%type <integer> optResultFaceType optResultColorType - -%type <integer> optTexImageUnitNum texImageUnitNum -%type <integer> optTexCoordUnitNum texCoordUnitNum -%type <integer> optLegacyTexUnitNum legacyTexUnitNum -%type <integer> texImageUnit texTarget -%type <integer> vtxAttribNum - -%type <attrib> attribBinding vtxAttribItem fragAttribItem - -%type <temp_sym> paramSingleInit paramSingleItemDecl -%type <integer> optArraySize - -%type <state> stateSingleItem stateMultipleItem -%type <state> stateMaterialItem -%type <state> stateLightItem stateLightModelItem stateLightProdItem -%type <state> stateTexGenItem stateFogItem stateClipPlaneItem statePointItem -%type <state> stateMatrixItem stateMatrixRow stateMatrixRows -%type <state> stateTexEnvItem stateDepthItem - -%type <state> stateLModProperty -%type <state> stateMatrixName optMatrixRows - -%type <integer> stateMatProperty -%type <integer> stateLightProperty stateSpotProperty -%type <integer> stateLightNumber stateLProdProperty -%type <integer> stateTexGenType stateTexGenCoord -%type <integer> stateTexEnvProperty -%type <integer> stateFogProperty -%type <integer> stateClipPlaneNum -%type <integer> statePointProperty - -%type <integer> stateOptMatModifier stateMatModifier stateMatrixRowNum -%type <integer> stateOptModMatNum stateModMatNum statePaletteMatNum -%type <integer> stateProgramMatNum - -%type <integer> ambDiffSpecProperty - -%type <state> programSingleItem progEnvParam progLocalParam -%type <state> programMultipleItem progEnvParams progLocalParams - -%type <temp_sym> paramMultipleInit paramMultInitList paramMultipleItem -%type <temp_sym> paramSingleItemUse - -%type <integer> progEnvParamNum progLocalParamNum -%type <state> progEnvParamNums progLocalParamNums - -%type <vector> paramConstDecl paramConstUse -%type <vector> paramConstScalarDecl paramConstScalarUse paramConstVector -%type <real> signedFloatConstant -%type <negate> optionalSign - -%{ -extern int yylex(YYSTYPE *yylval_param, YYLTYPE *yylloc_param, - void *yyscanner); -%} - -%% - -program: language optionSequence statementSequence END - ; - -language: ARBvp_10 - { - if (state->prog->Target != GL_VERTEX_PROGRAM_ARB) { - yyerror(& @1, state, "invalid fragment program header"); - - } - state->mode = ARB_vertex; - } - | ARBfp_10 - { - if (state->prog->Target != GL_FRAGMENT_PROGRAM_ARB) { - yyerror(& @1, state, "invalid vertex program header"); - } - state->mode = ARB_fragment; - - state->option.TexRect = - (state->ctx->Extensions.NV_texture_rectangle != GL_FALSE); - } - ; - -optionSequence: optionSequence option - | - ; - -option: OPTION string ';' - { - int valid = 0; - - if (state->mode == ARB_vertex) { - valid = _mesa_ARBvp_parse_option(state, $2); - } else if (state->mode == ARB_fragment) { - valid = _mesa_ARBfp_parse_option(state, $2); - } - - - free($2); - - if (!valid) { - const char *const err_str = (state->mode == ARB_vertex) - ? "invalid ARB vertex program option" - : "invalid ARB fragment program option"; - - yyerror(& @2, state, err_str); - YYERROR; - } - } - ; - -statementSequence: statementSequence statement - | - ; - -statement: instruction ';' - { - if ($1 != NULL) { - if (state->inst_tail == NULL) { - state->inst_head = $1; - } else { - state->inst_tail->next = $1; - } - - state->inst_tail = $1; - $1->next = NULL; - - state->prog->NumInstructions++; - } - } - | namingStatement ';' - ; - -instruction: ALU_instruction - { - $$ = $1; - state->prog->NumAluInstructions++; - } - | TexInstruction - { - $$ = $1; - state->prog->NumTexInstructions++; - } - ; - -ALU_instruction: ARL_instruction - | VECTORop_instruction - | SCALARop_instruction - | BINSCop_instruction - | BINop_instruction - | TRIop_instruction - | SWZ_instruction - ; - -TexInstruction: SAMPLE_instruction - | KIL_instruction - | TXD_instruction - ; - -ARL_instruction: ARL maskedAddrReg ',' scalarSrcReg - { - $$ = asm_instruction_ctor(OPCODE_ARL, & $2, & $4, NULL, NULL); - } - ; - -VECTORop_instruction: VECTOR_OP maskedDstReg ',' swizzleSrcReg - { - $$ = asm_instruction_copy_ctor(& $1, & $2, & $4, NULL, NULL); - } - ; - -SCALARop_instruction: SCALAR_OP maskedDstReg ',' scalarSrcReg - { - $$ = asm_instruction_copy_ctor(& $1, & $2, & $4, NULL, NULL); - } - ; - -BINSCop_instruction: BINSC_OP maskedDstReg ',' scalarSrcReg ',' scalarSrcReg - { - $$ = asm_instruction_copy_ctor(& $1, & $2, & $4, & $6, NULL); - } - ; - - -BINop_instruction: BIN_OP maskedDstReg ',' swizzleSrcReg ',' swizzleSrcReg - { - $$ = asm_instruction_copy_ctor(& $1, & $2, & $4, & $6, NULL); - } - ; - -TRIop_instruction: TRI_OP maskedDstReg ',' - swizzleSrcReg ',' swizzleSrcReg ',' swizzleSrcReg - { - $$ = asm_instruction_copy_ctor(& $1, & $2, & $4, & $6, & $8); - } - ; - -SAMPLE_instruction: SAMPLE_OP maskedDstReg ',' swizzleSrcReg ',' texImageUnit ',' texTarget - { - $$ = asm_instruction_copy_ctor(& $1, & $2, & $4, NULL, NULL); - if ($$ != NULL) { - const GLbitfield tex_mask = (1U << $6); - GLbitfield shadow_tex = 0; - GLbitfield target_mask = 0; - - - $$->Base.TexSrcUnit = $6; - - if ($8 < 0) { - shadow_tex = tex_mask; - - $$->Base.TexSrcTarget = -$8; - $$->Base.TexShadow = 1; - } else { - $$->Base.TexSrcTarget = $8; - } - - target_mask = (1U << $$->Base.TexSrcTarget); - - /* If this texture unit was previously accessed and that access - * had a different texture target, generate an error. - * - * If this texture unit was previously accessed and that access - * had a different shadow mode, generate an error. - */ - if ((state->prog->TexturesUsed[$6] != 0) - && ((state->prog->TexturesUsed[$6] != target_mask) - || ((state->prog->ShadowSamplers & tex_mask) - != shadow_tex))) { - yyerror(& @8, state, - "multiple targets used on one texture image unit"); - YYERROR; - } - - - state->prog->TexturesUsed[$6] |= target_mask; - state->prog->ShadowSamplers |= shadow_tex; - } - } - ; - -KIL_instruction: KIL swizzleSrcReg - { - $$ = asm_instruction_ctor(OPCODE_KIL, NULL, & $2, NULL, NULL); - state->fragment.UsesKill = 1; - } - | KIL ccTest - { - $$ = asm_instruction_ctor(OPCODE_KIL_NV, NULL, NULL, NULL, NULL); - $$->Base.DstReg.CondMask = $2.CondMask; - $$->Base.DstReg.CondSwizzle = $2.CondSwizzle; - $$->Base.DstReg.CondSrc = $2.CondSrc; - state->fragment.UsesKill = 1; - } - ; - -TXD_instruction: TXD_OP maskedDstReg ',' swizzleSrcReg ',' swizzleSrcReg ',' swizzleSrcReg ',' texImageUnit ',' texTarget - { - $$ = asm_instruction_copy_ctor(& $1, & $2, & $4, & $6, & $8); - if ($$ != NULL) { - const GLbitfield tex_mask = (1U << $10); - GLbitfield shadow_tex = 0; - GLbitfield target_mask = 0; - - - $$->Base.TexSrcUnit = $10; - - if ($12 < 0) { - shadow_tex = tex_mask; - - $$->Base.TexSrcTarget = -$12; - $$->Base.TexShadow = 1; - } else { - $$->Base.TexSrcTarget = $12; - } - - target_mask = (1U << $$->Base.TexSrcTarget); - - /* If this texture unit was previously accessed and that access - * had a different texture target, generate an error. - * - * If this texture unit was previously accessed and that access - * had a different shadow mode, generate an error. - */ - if ((state->prog->TexturesUsed[$10] != 0) - && ((state->prog->TexturesUsed[$10] != target_mask) - || ((state->prog->ShadowSamplers & tex_mask) - != shadow_tex))) { - yyerror(& @12, state, - "multiple targets used on one texture image unit"); - YYERROR; - } - - - state->prog->TexturesUsed[$10] |= target_mask; - state->prog->ShadowSamplers |= shadow_tex; - } - } - ; - -texImageUnit: TEXTURE_UNIT optTexImageUnitNum - { - $$ = $2; - } - ; - -texTarget: TEX_1D { $$ = TEXTURE_1D_INDEX; } - | TEX_2D { $$ = TEXTURE_2D_INDEX; } - | TEX_3D { $$ = TEXTURE_3D_INDEX; } - | TEX_CUBE { $$ = TEXTURE_CUBE_INDEX; } - | TEX_RECT { $$ = TEXTURE_RECT_INDEX; } - | TEX_SHADOW1D { $$ = -TEXTURE_1D_INDEX; } - | TEX_SHADOW2D { $$ = -TEXTURE_2D_INDEX; } - | TEX_SHADOWRECT { $$ = -TEXTURE_RECT_INDEX; } - | TEX_ARRAY1D { $$ = TEXTURE_1D_ARRAY_INDEX; } - | TEX_ARRAY2D { $$ = TEXTURE_2D_ARRAY_INDEX; } - | TEX_ARRAYSHADOW1D { $$ = -TEXTURE_1D_ARRAY_INDEX; } - | TEX_ARRAYSHADOW2D { $$ = -TEXTURE_2D_ARRAY_INDEX; } - ; - -SWZ_instruction: SWZ maskedDstReg ',' srcReg ',' extendedSwizzle - { - /* FIXME: Is this correct? Should the extenedSwizzle be applied - * FIXME: to the existing swizzle? - */ - $4.Base.Swizzle = $6.swizzle; - $4.Base.Negate = $6.mask; - - $$ = asm_instruction_copy_ctor(& $1, & $2, & $4, NULL, NULL); - } - ; - -scalarSrcReg: optionalSign scalarUse - { - $$ = $2; - - if ($1) { - $$.Base.Negate = ~$$.Base.Negate; - } - } - | optionalSign '|' scalarUse '|' - { - $$ = $3; - - if (!state->option.NV_fragment) { - yyerror(& @2, state, "unexpected character '|'"); - YYERROR; - } - - if ($1) { - $$.Base.Negate = ~$$.Base.Negate; - } - - $$.Base.Abs = 1; - } - ; - -scalarUse: srcReg scalarSuffix - { - $$ = $1; - - $$.Base.Swizzle = _mesa_combine_swizzles($$.Base.Swizzle, - $2.swizzle); - } - | paramConstScalarUse - { - struct asm_symbol temp_sym; - - if (!state->option.NV_fragment) { - yyerror(& @1, state, "expected scalar suffix"); - YYERROR; - } - - memset(& temp_sym, 0, sizeof(temp_sym)); - temp_sym.param_binding_begin = ~0; - initialize_symbol_from_const(state->prog, & temp_sym, & $1, GL_TRUE); - - set_src_reg_swz(& $$, PROGRAM_CONSTANT, - temp_sym.param_binding_begin, - temp_sym.param_binding_swizzle); - } - ; - -swizzleSrcReg: optionalSign srcReg swizzleSuffix - { - $$ = $2; - - if ($1) { - $$.Base.Negate = ~$$.Base.Negate; - } - - $$.Base.Swizzle = _mesa_combine_swizzles($$.Base.Swizzle, - $3.swizzle); - } - | optionalSign '|' srcReg swizzleSuffix '|' - { - $$ = $3; - - if (!state->option.NV_fragment) { - yyerror(& @2, state, "unexpected character '|'"); - YYERROR; - } - - if ($1) { - $$.Base.Negate = ~$$.Base.Negate; - } - - $$.Base.Abs = 1; - $$.Base.Swizzle = _mesa_combine_swizzles($$.Base.Swizzle, - $4.swizzle); - } - - ; - -maskedDstReg: dstReg optionalMask optionalCcMask - { - $$ = $1; - $$.WriteMask = $2.mask; - $$.CondMask = $3.CondMask; - $$.CondSwizzle = $3.CondSwizzle; - $$.CondSrc = $3.CondSrc; - - if ($$.File == PROGRAM_OUTPUT) { - /* Technically speaking, this should check that it is in - * vertex program mode. However, PositionInvariant can never be - * set in fragment program mode, so it is somewhat irrelevant. - */ - if (state->option.PositionInvariant - && ($$.Index == VERT_RESULT_HPOS)) { - yyerror(& @1, state, "position-invariant programs cannot " - "write position"); - YYERROR; - } - - state->prog->OutputsWritten |= BITFIELD64_BIT($$.Index); - } - } - ; - -maskedAddrReg: addrReg addrWriteMask - { - set_dst_reg(& $$, PROGRAM_ADDRESS, 0); - $$.WriteMask = $2.mask; - } - ; - -extendedSwizzle: extSwizComp ',' extSwizComp ',' extSwizComp ',' extSwizComp - { - const unsigned xyzw_valid = - ($1.xyzw_valid << 0) - | ($3.xyzw_valid << 1) - | ($5.xyzw_valid << 2) - | ($7.xyzw_valid << 3); - const unsigned rgba_valid = - ($1.rgba_valid << 0) - | ($3.rgba_valid << 1) - | ($5.rgba_valid << 2) - | ($7.rgba_valid << 3); - - /* All of the swizzle components have to be valid in either RGBA - * or XYZW. Note that 0 and 1 are valid in both, so both masks - * can have some bits set. - * - * We somewhat deviate from the spec here. It would be really hard - * to figure out which component is the error, and there probably - * isn't a lot of benefit. - */ - if ((rgba_valid != 0x0f) && (xyzw_valid != 0x0f)) { - yyerror(& @1, state, "cannot combine RGBA and XYZW swizzle " - "components"); - YYERROR; - } - - $$.swizzle = MAKE_SWIZZLE4($1.swz, $3.swz, $5.swz, $7.swz); - $$.mask = ($1.negate) | ($3.negate << 1) | ($5.negate << 2) - | ($7.negate << 3); - } - ; - -extSwizComp: optionalSign extSwizSel - { - $$ = $2; - $$.negate = ($1) ? 1 : 0; - } - ; - -extSwizSel: INTEGER - { - if (($1 != 0) && ($1 != 1)) { - yyerror(& @1, state, "invalid extended swizzle selector"); - YYERROR; - } - - $$.swz = ($1 == 0) ? SWIZZLE_ZERO : SWIZZLE_ONE; - - /* 0 and 1 are valid for both RGBA swizzle names and XYZW - * swizzle names. - */ - $$.xyzw_valid = 1; - $$.rgba_valid = 1; - } - | string - { - char s; - - if (strlen($1) > 1) { - yyerror(& @1, state, "invalid extended swizzle selector"); - YYERROR; - } - - s = $1[0]; - free($1); - - switch (s) { - case 'x': - $$.swz = SWIZZLE_X; - $$.xyzw_valid = 1; - break; - case 'y': - $$.swz = SWIZZLE_Y; - $$.xyzw_valid = 1; - break; - case 'z': - $$.swz = SWIZZLE_Z; - $$.xyzw_valid = 1; - break; - case 'w': - $$.swz = SWIZZLE_W; - $$.xyzw_valid = 1; - break; - - case 'r': - $$.swz = SWIZZLE_X; - $$.rgba_valid = 1; - break; - case 'g': - $$.swz = SWIZZLE_Y; - $$.rgba_valid = 1; - break; - case 'b': - $$.swz = SWIZZLE_Z; - $$.rgba_valid = 1; - break; - case 'a': - $$.swz = SWIZZLE_W; - $$.rgba_valid = 1; - break; - - default: - yyerror(& @1, state, "invalid extended swizzle selector"); - YYERROR; - break; - } - } - ; - -srcReg: USED_IDENTIFIER /* temporaryReg | progParamSingle */ - { - struct asm_symbol *const s = (struct asm_symbol *) - _mesa_symbol_table_find_symbol(state->st, 0, $1); - - free($1); - - if (s == NULL) { - yyerror(& @1, state, "invalid operand variable"); - YYERROR; - } else if ((s->type != at_param) && (s->type != at_temp) - && (s->type != at_attrib)) { - yyerror(& @1, state, "invalid operand variable"); - YYERROR; - } else if ((s->type == at_param) && s->param_is_array) { - yyerror(& @1, state, "non-array access to array PARAM"); - YYERROR; - } - - init_src_reg(& $$); - switch (s->type) { - case at_temp: - set_src_reg(& $$, PROGRAM_TEMPORARY, s->temp_binding); - break; - case at_param: - set_src_reg_swz(& $$, s->param_binding_type, - s->param_binding_begin, - s->param_binding_swizzle); - break; - case at_attrib: - set_src_reg(& $$, PROGRAM_INPUT, s->attrib_binding); - state->prog->InputsRead |= (1U << $$.Base.Index); - - if (!validate_inputs(& @1, state)) { - YYERROR; - } - break; - - default: - YYERROR; - break; - } - } - | attribBinding - { - set_src_reg(& $$, PROGRAM_INPUT, $1); - state->prog->InputsRead |= (1U << $$.Base.Index); - - if (!validate_inputs(& @1, state)) { - YYERROR; - } - } - | progParamArray '[' progParamArrayMem ']' - { - if (! $3.Base.RelAddr - && ((unsigned) $3.Base.Index >= $1->param_binding_length)) { - yyerror(& @3, state, "out of bounds array access"); - YYERROR; - } - - init_src_reg(& $$); - $$.Base.File = $1->param_binding_type; - - if ($3.Base.RelAddr) { - state->prog->IndirectRegisterFiles |= (1 << $$.Base.File); - $1->param_accessed_indirectly = 1; - - $$.Base.RelAddr = 1; - $$.Base.Index = $3.Base.Index; - $$.Symbol = $1; - } else { - $$.Base.Index = $1->param_binding_begin + $3.Base.Index; - } - } - | paramSingleItemUse - { - gl_register_file file = ($1.name != NULL) - ? $1.param_binding_type - : PROGRAM_CONSTANT; - set_src_reg_swz(& $$, file, $1.param_binding_begin, - $1.param_binding_swizzle); - } - ; - -dstReg: resultBinding - { - set_dst_reg(& $$, PROGRAM_OUTPUT, $1); - } - | USED_IDENTIFIER /* temporaryReg | vertexResultReg */ - { - struct asm_symbol *const s = (struct asm_symbol *) - _mesa_symbol_table_find_symbol(state->st, 0, $1); - - free($1); - - if (s == NULL) { - yyerror(& @1, state, "invalid operand variable"); - YYERROR; - } else if ((s->type != at_output) && (s->type != at_temp)) { - yyerror(& @1, state, "invalid operand variable"); - YYERROR; - } - - switch (s->type) { - case at_temp: - set_dst_reg(& $$, PROGRAM_TEMPORARY, s->temp_binding); - break; - case at_output: - set_dst_reg(& $$, PROGRAM_OUTPUT, s->output_binding); - break; - default: - set_dst_reg(& $$, s->param_binding_type, s->param_binding_begin); - break; - } - } - ; - -progParamArray: USED_IDENTIFIER - { - struct asm_symbol *const s = (struct asm_symbol *) - _mesa_symbol_table_find_symbol(state->st, 0, $1); - - free($1); - - if (s == NULL) { - yyerror(& @1, state, "invalid operand variable"); - YYERROR; - } else if ((s->type != at_param) || !s->param_is_array) { - yyerror(& @1, state, "array access to non-PARAM variable"); - YYERROR; - } else { - $$ = s; - } - } - ; - -progParamArrayMem: progParamArrayAbs | progParamArrayRel; - -progParamArrayAbs: INTEGER - { - init_src_reg(& $$); - $$.Base.Index = $1; - } - ; - -progParamArrayRel: addrReg addrComponent addrRegRelOffset - { - /* FINISHME: Add support for multiple address registers. - */ - /* FINISHME: Add support for 4-component address registers. - */ - init_src_reg(& $$); - $$.Base.RelAddr = 1; - $$.Base.Index = $3; - } - ; - -addrRegRelOffset: { $$ = 0; } - | '+' addrRegPosOffset { $$ = $2; } - | '-' addrRegNegOffset { $$ = -$2; } - ; - -addrRegPosOffset: INTEGER - { - if (($1 < 0) || ($1 > (state->limits->MaxAddressOffset - 1))) { - char s[100]; - _mesa_snprintf(s, sizeof(s), - "relative address offset too large (%d)", $1); - yyerror(& @1, state, s); - YYERROR; - } else { - $$ = $1; - } - } - ; - -addrRegNegOffset: INTEGER - { - if (($1 < 0) || ($1 > state->limits->MaxAddressOffset)) { - char s[100]; - _mesa_snprintf(s, sizeof(s), - "relative address offset too large (%d)", $1); - yyerror(& @1, state, s); - YYERROR; - } else { - $$ = $1; - } - } - ; - -addrReg: USED_IDENTIFIER - { - struct asm_symbol *const s = (struct asm_symbol *) - _mesa_symbol_table_find_symbol(state->st, 0, $1); - - free($1); - - if (s == NULL) { - yyerror(& @1, state, "invalid array member"); - YYERROR; - } else if (s->type != at_address) { - yyerror(& @1, state, - "invalid variable for indexed array access"); - YYERROR; - } else { - $$ = s; - } - } - ; - -addrComponent: MASK1 - { - if ($1.mask != WRITEMASK_X) { - yyerror(& @1, state, "invalid address component selector"); - YYERROR; - } else { - $$ = $1; - } - } - ; - -addrWriteMask: MASK1 - { - if ($1.mask != WRITEMASK_X) { - yyerror(& @1, state, - "address register write mask must be \".x\""); - YYERROR; - } else { - $$ = $1; - } - } - ; - -scalarSuffix: MASK1; - -swizzleSuffix: MASK1 - | MASK4 - | SWIZZLE - | { $$.swizzle = SWIZZLE_NOOP; $$.mask = WRITEMASK_XYZW; } - ; - -optionalMask: MASK4 | MASK3 | MASK2 | MASK1 - | { $$.swizzle = SWIZZLE_NOOP; $$.mask = WRITEMASK_XYZW; } - ; - -optionalCcMask: '(' ccTest ')' - { - $$ = $2; - } - | '(' ccTest2 ')' - { - $$ = $2; - } - | - { - $$.CondMask = COND_TR; - $$.CondSwizzle = SWIZZLE_NOOP; - $$.CondSrc = 0; - } - ; - -ccTest: ccMaskRule swizzleSuffix - { - $$ = $1; - $$.CondSwizzle = $2.swizzle; - } - ; - -ccTest2: ccMaskRule2 swizzleSuffix - { - $$ = $1; - $$.CondSwizzle = $2.swizzle; - } - ; - -ccMaskRule: IDENTIFIER - { - const int cond = _mesa_parse_cc($1); - if ((cond == 0) || ($1[2] != '\0')) { - char *const err_str = - make_error_string("invalid condition code \"%s\"", $1); - - yyerror(& @1, state, (err_str != NULL) - ? err_str : "invalid condition code"); - - if (err_str != NULL) { - free(err_str); - } - - YYERROR; - } - - $$.CondMask = cond; - $$.CondSwizzle = SWIZZLE_NOOP; - $$.CondSrc = 0; - } - ; - -ccMaskRule2: USED_IDENTIFIER - { - const int cond = _mesa_parse_cc($1); - if ((cond == 0) || ($1[2] != '\0')) { - char *const err_str = - make_error_string("invalid condition code \"%s\"", $1); - - yyerror(& @1, state, (err_str != NULL) - ? err_str : "invalid condition code"); - - if (err_str != NULL) { - free(err_str); - } - - YYERROR; - } - - $$.CondMask = cond; - $$.CondSwizzle = SWIZZLE_NOOP; - $$.CondSrc = 0; - } - ; - -namingStatement: ATTRIB_statement - | PARAM_statement - | TEMP_statement - | ADDRESS_statement - | OUTPUT_statement - | ALIAS_statement - ; - -ATTRIB_statement: ATTRIB IDENTIFIER '=' attribBinding - { - struct asm_symbol *const s = - declare_variable(state, $2, at_attrib, & @2); - - if (s == NULL) { - free($2); - YYERROR; - } else { - s->attrib_binding = $4; - state->InputsBound |= (1U << s->attrib_binding); - - if (!validate_inputs(& @4, state)) { - YYERROR; - } - } - } - ; - -attribBinding: VERTEX vtxAttribItem - { - $$ = $2; - } - | FRAGMENT fragAttribItem - { - $$ = $2; - } - ; - -vtxAttribItem: POSITION - { - $$ = VERT_ATTRIB_POS; - } - | WEIGHT vtxOptWeightNum - { - $$ = VERT_ATTRIB_WEIGHT; - } - | NORMAL - { - $$ = VERT_ATTRIB_NORMAL; - } - | COLOR optColorType - { - if (!state->ctx->Extensions.EXT_secondary_color) { - yyerror(& @2, state, "GL_EXT_secondary_color not supported"); - YYERROR; - } - - $$ = VERT_ATTRIB_COLOR0 + $2; - } - | FOGCOORD - { - if (!state->ctx->Extensions.EXT_fog_coord) { - yyerror(& @1, state, "GL_EXT_fog_coord not supported"); - YYERROR; - } - - $$ = VERT_ATTRIB_FOG; - } - | TEXCOORD optTexCoordUnitNum - { - $$ = VERT_ATTRIB_TEX0 + $2; - } - | MATRIXINDEX '[' vtxWeightNum ']' - { - yyerror(& @1, state, "GL_ARB_matrix_palette not supported"); - YYERROR; - } - | VTXATTRIB '[' vtxAttribNum ']' - { - $$ = VERT_ATTRIB_GENERIC0 + $3; - } - ; - -vtxAttribNum: INTEGER - { - if ((unsigned) $1 >= state->limits->MaxAttribs) { - yyerror(& @1, state, "invalid vertex attribute reference"); - YYERROR; - } - - $$ = $1; - } - ; - -vtxOptWeightNum: | '[' vtxWeightNum ']'; -vtxWeightNum: INTEGER; - -fragAttribItem: POSITION - { - $$ = FRAG_ATTRIB_WPOS; - } - | COLOR optColorType - { - $$ = FRAG_ATTRIB_COL0 + $2; - } - | FOGCOORD - { - $$ = FRAG_ATTRIB_FOGC; - } - | TEXCOORD optTexCoordUnitNum - { - $$ = FRAG_ATTRIB_TEX0 + $2; - } - ; - -PARAM_statement: PARAM_singleStmt | PARAM_multipleStmt; - -PARAM_singleStmt: PARAM IDENTIFIER paramSingleInit - { - struct asm_symbol *const s = - declare_variable(state, $2, at_param, & @2); - - if (s == NULL) { - free($2); - YYERROR; - } else { - s->param_binding_type = $3.param_binding_type; - s->param_binding_begin = $3.param_binding_begin; - s->param_binding_length = $3.param_binding_length; - s->param_binding_swizzle = $3.param_binding_swizzle; - s->param_is_array = 0; - } - } - ; - -PARAM_multipleStmt: PARAM IDENTIFIER '[' optArraySize ']' paramMultipleInit - { - if (($4 != 0) && ((unsigned) $4 != $6.param_binding_length)) { - free($2); - yyerror(& @4, state, - "parameter array size and number of bindings must match"); - YYERROR; - } else { - struct asm_symbol *const s = - declare_variable(state, $2, $6.type, & @2); - - if (s == NULL) { - free($2); - YYERROR; - } else { - s->param_binding_type = $6.param_binding_type; - s->param_binding_begin = $6.param_binding_begin; - s->param_binding_length = $6.param_binding_length; - s->param_binding_swizzle = SWIZZLE_XYZW; - s->param_is_array = 1; - } - } - } - ; - -optArraySize: - { - $$ = 0; - } - | INTEGER - { - if (($1 < 1) || ((unsigned) $1 > state->limits->MaxParameters)) { - char msg[100]; - _mesa_snprintf(msg, sizeof(msg), - "invalid parameter array size (size=%d max=%u)", - $1, state->limits->MaxParameters); - yyerror(& @1, state, msg); - YYERROR; - } else { - $$ = $1; - } - } - ; - -paramSingleInit: '=' paramSingleItemDecl - { - $$ = $2; - } - ; - -paramMultipleInit: '=' '{' paramMultInitList '}' - { - $$ = $3; - } - ; - -paramMultInitList: paramMultipleItem - | paramMultInitList ',' paramMultipleItem - { - $1.param_binding_length += $3.param_binding_length; - $$ = $1; - } - ; - -paramSingleItemDecl: stateSingleItem - { - memset(& $$, 0, sizeof($$)); - $$.param_binding_begin = ~0; - initialize_symbol_from_state(state->prog, & $$, $1); - } - | programSingleItem - { - memset(& $$, 0, sizeof($$)); - $$.param_binding_begin = ~0; - initialize_symbol_from_param(state->prog, & $$, $1); - } - | paramConstDecl - { - memset(& $$, 0, sizeof($$)); - $$.param_binding_begin = ~0; - initialize_symbol_from_const(state->prog, & $$, & $1, GL_TRUE); - } - ; - -paramSingleItemUse: stateSingleItem - { - memset(& $$, 0, sizeof($$)); - $$.param_binding_begin = ~0; - initialize_symbol_from_state(state->prog, & $$, $1); - } - | programSingleItem - { - memset(& $$, 0, sizeof($$)); - $$.param_binding_begin = ~0; - initialize_symbol_from_param(state->prog, & $$, $1); - } - | paramConstUse - { - memset(& $$, 0, sizeof($$)); - $$.param_binding_begin = ~0; - initialize_symbol_from_const(state->prog, & $$, & $1, GL_TRUE); - } - ; - -paramMultipleItem: stateMultipleItem - { - memset(& $$, 0, sizeof($$)); - $$.param_binding_begin = ~0; - initialize_symbol_from_state(state->prog, & $$, $1); - } - | programMultipleItem - { - memset(& $$, 0, sizeof($$)); - $$.param_binding_begin = ~0; - initialize_symbol_from_param(state->prog, & $$, $1); - } - | paramConstDecl - { - memset(& $$, 0, sizeof($$)); - $$.param_binding_begin = ~0; - initialize_symbol_from_const(state->prog, & $$, & $1, GL_FALSE); - } - ; - -stateMultipleItem: stateSingleItem { memcpy($$, $1, sizeof($$)); } - | STATE stateMatrixRows { memcpy($$, $2, sizeof($$)); } - ; - -stateSingleItem: STATE stateMaterialItem { memcpy($$, $2, sizeof($$)); } - | STATE stateLightItem { memcpy($$, $2, sizeof($$)); } - | STATE stateLightModelItem { memcpy($$, $2, sizeof($$)); } - | STATE stateLightProdItem { memcpy($$, $2, sizeof($$)); } - | STATE stateTexGenItem { memcpy($$, $2, sizeof($$)); } - | STATE stateTexEnvItem { memcpy($$, $2, sizeof($$)); } - | STATE stateFogItem { memcpy($$, $2, sizeof($$)); } - | STATE stateClipPlaneItem { memcpy($$, $2, sizeof($$)); } - | STATE statePointItem { memcpy($$, $2, sizeof($$)); } - | STATE stateMatrixRow { memcpy($$, $2, sizeof($$)); } - | STATE stateDepthItem { memcpy($$, $2, sizeof($$)); } - ; - -stateMaterialItem: MATERIAL optFaceType stateMatProperty - { - memset($$, 0, sizeof($$)); - $$[0] = STATE_MATERIAL; - $$[1] = $2; - $$[2] = $3; - } - ; - -stateMatProperty: ambDiffSpecProperty - { - $$ = $1; - } - | EMISSION - { - $$ = STATE_EMISSION; - } - | SHININESS - { - $$ = STATE_SHININESS; - } - ; - -stateLightItem: LIGHT '[' stateLightNumber ']' stateLightProperty - { - memset($$, 0, sizeof($$)); - $$[0] = STATE_LIGHT; - $$[1] = $3; - $$[2] = $5; - } - ; - -stateLightProperty: ambDiffSpecProperty - { - $$ = $1; - } - | POSITION - { - $$ = STATE_POSITION; - } - | ATTENUATION - { - if (!state->ctx->Extensions.EXT_point_parameters) { - yyerror(& @1, state, "GL_ARB_point_parameters not supported"); - YYERROR; - } - - $$ = STATE_ATTENUATION; - } - | SPOT stateSpotProperty - { - $$ = $2; - } - | HALF - { - $$ = STATE_HALF_VECTOR; - } - ; - -stateSpotProperty: DIRECTION - { - $$ = STATE_SPOT_DIRECTION; - } - ; - -stateLightModelItem: LIGHTMODEL stateLModProperty - { - $$[0] = $2[0]; - $$[1] = $2[1]; - } - ; - -stateLModProperty: AMBIENT - { - memset($$, 0, sizeof($$)); - $$[0] = STATE_LIGHTMODEL_AMBIENT; - } - | optFaceType SCENECOLOR - { - memset($$, 0, sizeof($$)); - $$[0] = STATE_LIGHTMODEL_SCENECOLOR; - $$[1] = $1; - } - ; - -stateLightProdItem: LIGHTPROD '[' stateLightNumber ']' optFaceType stateLProdProperty - { - memset($$, 0, sizeof($$)); - $$[0] = STATE_LIGHTPROD; - $$[1] = $3; - $$[2] = $5; - $$[3] = $6; - } - ; - -stateLProdProperty: ambDiffSpecProperty; - -stateTexEnvItem: TEXENV optLegacyTexUnitNum stateTexEnvProperty - { - memset($$, 0, sizeof($$)); - $$[0] = $3; - $$[1] = $2; - } - ; - -stateTexEnvProperty: COLOR - { - $$ = STATE_TEXENV_COLOR; - } - ; - -ambDiffSpecProperty: AMBIENT - { - $$ = STATE_AMBIENT; - } - | DIFFUSE - { - $$ = STATE_DIFFUSE; - } - | SPECULAR - { - $$ = STATE_SPECULAR; - } - ; - -stateLightNumber: INTEGER - { - if ((unsigned) $1 >= state->MaxLights) { - yyerror(& @1, state, "invalid light selector"); - YYERROR; - } - - $$ = $1; - } - ; - -stateTexGenItem: TEXGEN optTexCoordUnitNum stateTexGenType stateTexGenCoord - { - memset($$, 0, sizeof($$)); - $$[0] = STATE_TEXGEN; - $$[1] = $2; - $$[2] = $3 + $4; - } - ; - -stateTexGenType: EYE - { - $$ = STATE_TEXGEN_EYE_S; - } - | OBJECT - { - $$ = STATE_TEXGEN_OBJECT_S; - } - ; -stateTexGenCoord: TEXGEN_S - { - $$ = STATE_TEXGEN_EYE_S - STATE_TEXGEN_EYE_S; - } - | TEXGEN_T - { - $$ = STATE_TEXGEN_EYE_T - STATE_TEXGEN_EYE_S; - } - | TEXGEN_R - { - $$ = STATE_TEXGEN_EYE_R - STATE_TEXGEN_EYE_S; - } - | TEXGEN_Q - { - $$ = STATE_TEXGEN_EYE_Q - STATE_TEXGEN_EYE_S; - } - ; - -stateFogItem: FOG stateFogProperty - { - memset($$, 0, sizeof($$)); - $$[0] = $2; - } - ; - -stateFogProperty: COLOR - { - $$ = STATE_FOG_COLOR; - } - | PARAMS - { - $$ = STATE_FOG_PARAMS; - } - ; - -stateClipPlaneItem: CLIP '[' stateClipPlaneNum ']' PLANE - { - memset($$, 0, sizeof($$)); - $$[0] = STATE_CLIPPLANE; - $$[1] = $3; - } - ; - -stateClipPlaneNum: INTEGER - { - if ((unsigned) $1 >= state->MaxClipPlanes) { - yyerror(& @1, state, "invalid clip plane selector"); - YYERROR; - } - - $$ = $1; - } - ; - -statePointItem: POINT_TOK statePointProperty - { - memset($$, 0, sizeof($$)); - $$[0] = $2; - } - ; - -statePointProperty: SIZE_TOK - { - $$ = STATE_POINT_SIZE; - } - | ATTENUATION - { - $$ = STATE_POINT_ATTENUATION; - } - ; - -stateMatrixRow: stateMatrixItem ROW '[' stateMatrixRowNum ']' - { - $$[0] = $1[0]; - $$[1] = $1[1]; - $$[2] = $4; - $$[3] = $4; - $$[4] = $1[2]; - } - ; - -stateMatrixRows: stateMatrixItem optMatrixRows - { - $$[0] = $1[0]; - $$[1] = $1[1]; - $$[2] = $2[2]; - $$[3] = $2[3]; - $$[4] = $1[2]; - } - ; - -optMatrixRows: - { - $$[2] = 0; - $$[3] = 3; - } - | ROW '[' stateMatrixRowNum DOT_DOT stateMatrixRowNum ']' - { - /* It seems logical that the matrix row range specifier would have - * to specify a range or more than one row (i.e., $5 > $3). - * However, the ARB_vertex_program spec says "a program will fail - * to load if <a> is greater than <b>." This means that $3 == $5 - * is valid. - */ - if ($3 > $5) { - yyerror(& @3, state, "invalid matrix row range"); - YYERROR; - } - - $$[2] = $3; - $$[3] = $5; - } - ; - -stateMatrixItem: MATRIX stateMatrixName stateOptMatModifier - { - $$[0] = $2[0]; - $$[1] = $2[1]; - $$[2] = $3; - } - ; - -stateOptMatModifier: - { - $$ = 0; - } - | stateMatModifier - { - $$ = $1; - } - ; - -stateMatModifier: INVERSE - { - $$ = STATE_MATRIX_INVERSE; - } - | TRANSPOSE - { - $$ = STATE_MATRIX_TRANSPOSE; - } - | INVTRANS - { - $$ = STATE_MATRIX_INVTRANS; - } - ; - -stateMatrixRowNum: INTEGER - { - if ($1 > 3) { - yyerror(& @1, state, "invalid matrix row reference"); - YYERROR; - } - - $$ = $1; - } - ; - -stateMatrixName: MODELVIEW stateOptModMatNum - { - $$[0] = STATE_MODELVIEW_MATRIX; - $$[1] = $2; - } - | PROJECTION - { - $$[0] = STATE_PROJECTION_MATRIX; - $$[1] = 0; - } - | MVP - { - $$[0] = STATE_MVP_MATRIX; - $$[1] = 0; - } - | TEXTURE optTexCoordUnitNum - { - $$[0] = STATE_TEXTURE_MATRIX; - $$[1] = $2; - } - | PALETTE '[' statePaletteMatNum ']' - { - yyerror(& @1, state, "GL_ARB_matrix_palette not supported"); - YYERROR; - } - | MAT_PROGRAM '[' stateProgramMatNum ']' - { - $$[0] = STATE_PROGRAM_MATRIX; - $$[1] = $3; - } - ; - -stateOptModMatNum: - { - $$ = 0; - } - | '[' stateModMatNum ']' - { - $$ = $2; - } - ; -stateModMatNum: INTEGER - { - /* Since GL_ARB_vertex_blend isn't supported, only modelview matrix - * zero is valid. - */ - if ($1 != 0) { - yyerror(& @1, state, "invalid modelview matrix index"); - YYERROR; - } - - $$ = $1; - } - ; -statePaletteMatNum: INTEGER - { - /* Since GL_ARB_matrix_palette isn't supported, just let any value - * through here. The error will be generated later. - */ - $$ = $1; - } - ; -stateProgramMatNum: INTEGER - { - if ((unsigned) $1 >= state->MaxProgramMatrices) { - yyerror(& @1, state, "invalid program matrix selector"); - YYERROR; - } - - $$ = $1; - } - ; - -stateDepthItem: DEPTH RANGE - { - memset($$, 0, sizeof($$)); - $$[0] = STATE_DEPTH_RANGE; - } - ; - - -programSingleItem: progEnvParam | progLocalParam; - -programMultipleItem: progEnvParams | progLocalParams; - -progEnvParams: PROGRAM ENV '[' progEnvParamNums ']' - { - memset($$, 0, sizeof($$)); - $$[0] = state->state_param_enum; - $$[1] = STATE_ENV; - $$[2] = $4[0]; - $$[3] = $4[1]; - } - ; - -progEnvParamNums: progEnvParamNum - { - $$[0] = $1; - $$[1] = $1; - } - | progEnvParamNum DOT_DOT progEnvParamNum - { - $$[0] = $1; - $$[1] = $3; - } - ; - -progEnvParam: PROGRAM ENV '[' progEnvParamNum ']' - { - memset($$, 0, sizeof($$)); - $$[0] = state->state_param_enum; - $$[1] = STATE_ENV; - $$[2] = $4; - $$[3] = $4; - } - ; - -progLocalParams: PROGRAM LOCAL '[' progLocalParamNums ']' - { - memset($$, 0, sizeof($$)); - $$[0] = state->state_param_enum; - $$[1] = STATE_LOCAL; - $$[2] = $4[0]; - $$[3] = $4[1]; - } - -progLocalParamNums: progLocalParamNum - { - $$[0] = $1; - $$[1] = $1; - } - | progLocalParamNum DOT_DOT progLocalParamNum - { - $$[0] = $1; - $$[1] = $3; - } - ; - -progLocalParam: PROGRAM LOCAL '[' progLocalParamNum ']' - { - memset($$, 0, sizeof($$)); - $$[0] = state->state_param_enum; - $$[1] = STATE_LOCAL; - $$[2] = $4; - $$[3] = $4; - } - ; - -progEnvParamNum: INTEGER - { - if ((unsigned) $1 >= state->limits->MaxEnvParams) { - yyerror(& @1, state, "invalid environment parameter reference"); - YYERROR; - } - $$ = $1; - } - ; - -progLocalParamNum: INTEGER - { - if ((unsigned) $1 >= state->limits->MaxLocalParams) { - yyerror(& @1, state, "invalid local parameter reference"); - YYERROR; - } - $$ = $1; - } - ; - - - -paramConstDecl: paramConstScalarDecl | paramConstVector; -paramConstUse: paramConstScalarUse | paramConstVector; - -paramConstScalarDecl: signedFloatConstant - { - $$.count = 4; - $$.data[0] = $1; - $$.data[1] = $1; - $$.data[2] = $1; - $$.data[3] = $1; - } - ; - -paramConstScalarUse: REAL - { - $$.count = 1; - $$.data[0] = $1; - $$.data[1] = $1; - $$.data[2] = $1; - $$.data[3] = $1; - } - | INTEGER - { - $$.count = 1; - $$.data[0] = (float) $1; - $$.data[1] = (float) $1; - $$.data[2] = (float) $1; - $$.data[3] = (float) $1; - } - ; - -paramConstVector: '{' signedFloatConstant '}' - { - $$.count = 4; - $$.data[0] = $2; - $$.data[1] = 0.0f; - $$.data[2] = 0.0f; - $$.data[3] = 1.0f; - } - | '{' signedFloatConstant ',' signedFloatConstant '}' - { - $$.count = 4; - $$.data[0] = $2; - $$.data[1] = $4; - $$.data[2] = 0.0f; - $$.data[3] = 1.0f; - } - | '{' signedFloatConstant ',' signedFloatConstant ',' - signedFloatConstant '}' - { - $$.count = 4; - $$.data[0] = $2; - $$.data[1] = $4; - $$.data[2] = $6; - $$.data[3] = 1.0f; - } - | '{' signedFloatConstant ',' signedFloatConstant ',' - signedFloatConstant ',' signedFloatConstant '}' - { - $$.count = 4; - $$.data[0] = $2; - $$.data[1] = $4; - $$.data[2] = $6; - $$.data[3] = $8; - } - ; - -signedFloatConstant: optionalSign REAL - { - $$ = ($1) ? -$2 : $2; - } - | optionalSign INTEGER - { - $$ = (float)(($1) ? -$2 : $2); - } - ; - -optionalSign: '+' { $$ = FALSE; } - | '-' { $$ = TRUE; } - | { $$ = FALSE; } - ; - -TEMP_statement: optVarSize TEMP { $<integer>$ = $2; } varNameList - ; - -optVarSize: string - { - /* NV_fragment_program_option defines the size qualifiers in a - * fairly broken way. "SHORT" or "LONG" can optionally be used - * before TEMP or OUTPUT. However, neither is a reserved word! - * This means that we have to parse it as an identifier, then check - * to make sure it's one of the valid values. *sigh* - * - * In addition, the grammar in the extension spec does *not* allow - * the size specifier to be optional, but all known implementations - * do. - */ - if (!state->option.NV_fragment) { - yyerror(& @1, state, "unexpected IDENTIFIER"); - YYERROR; - } - - if (strcmp("SHORT", $1) == 0) { - } else if (strcmp("LONG", $1) == 0) { - } else { - char *const err_str = - make_error_string("invalid storage size specifier \"%s\"", - $1); - - yyerror(& @1, state, (err_str != NULL) - ? err_str : "invalid storage size specifier"); - - if (err_str != NULL) { - free(err_str); - } - - YYERROR; - } - } - | - { - } - ; - -ADDRESS_statement: ADDRESS { $<integer>$ = $1; } varNameList - ; - -varNameList: varNameList ',' IDENTIFIER - { - if (!declare_variable(state, $3, $<integer>0, & @3)) { - free($3); - YYERROR; - } - } - | IDENTIFIER - { - if (!declare_variable(state, $1, $<integer>0, & @1)) { - free($1); - YYERROR; - } - } - ; - -OUTPUT_statement: optVarSize OUTPUT IDENTIFIER '=' resultBinding - { - struct asm_symbol *const s = - declare_variable(state, $3, at_output, & @3); - - if (s == NULL) { - free($3); - YYERROR; - } else { - s->output_binding = $5; - } - } - ; - -resultBinding: RESULT POSITION - { - if (state->mode == ARB_vertex) { - $$ = VERT_RESULT_HPOS; - } else { - yyerror(& @2, state, "invalid program result name"); - YYERROR; - } - } - | RESULT FOGCOORD - { - if (state->mode == ARB_vertex) { - $$ = VERT_RESULT_FOGC; - } else { - yyerror(& @2, state, "invalid program result name"); - YYERROR; - } - } - | RESULT resultColBinding - { - $$ = $2; - } - | RESULT POINTSIZE - { - if (state->mode == ARB_vertex) { - $$ = VERT_RESULT_PSIZ; - } else { - yyerror(& @2, state, "invalid program result name"); - YYERROR; - } - } - | RESULT TEXCOORD optTexCoordUnitNum - { - if (state->mode == ARB_vertex) { - $$ = VERT_RESULT_TEX0 + $3; - } else { - yyerror(& @2, state, "invalid program result name"); - YYERROR; - } - } - | RESULT DEPTH - { - if (state->mode == ARB_fragment) { - $$ = FRAG_RESULT_DEPTH; - } else { - yyerror(& @2, state, "invalid program result name"); - YYERROR; - } - } - ; - -resultColBinding: COLOR optResultFaceType optResultColorType - { - $$ = $2 + $3; - } - ; - -optResultFaceType: - { - if (state->mode == ARB_vertex) { - $$ = VERT_RESULT_COL0; - } else { - if (state->option.DrawBuffers) - $$ = FRAG_RESULT_DATA0; - else - $$ = FRAG_RESULT_COLOR; - } - } - | '[' INTEGER ']' - { - if (state->mode == ARB_vertex) { - yyerror(& @1, state, "invalid program result name"); - YYERROR; - } else { - if (!state->option.DrawBuffers) { - /* From the ARB_draw_buffers spec (same text exists - * for ATI_draw_buffers): - * - * If this option is not specified, a fragment - * program that attempts to bind - * "result.color[n]" will fail to load, and only - * "result.color" will be allowed. - */ - yyerror(& @1, state, - "result.color[] used without " - "`OPTION ARB_draw_buffers' or " - "`OPTION ATI_draw_buffers'"); - YYERROR; - } else if ($2 >= state->MaxDrawBuffers) { - yyerror(& @1, state, - "result.color[] exceeds MAX_DRAW_BUFFERS_ARB"); - YYERROR; - } - $$ = FRAG_RESULT_DATA0 + $2; - } - } - | FRONT - { - if (state->mode == ARB_vertex) { - $$ = VERT_RESULT_COL0; - } else { - yyerror(& @1, state, "invalid program result name"); - YYERROR; - } - } - | BACK - { - if (state->mode == ARB_vertex) { - $$ = VERT_RESULT_BFC0; - } else { - yyerror(& @1, state, "invalid program result name"); - YYERROR; - } - } - ; - -optResultColorType: - { - $$ = 0; - } - | PRIMARY - { - if (state->mode == ARB_vertex) { - $$ = 0; - } else { - yyerror(& @1, state, "invalid program result name"); - YYERROR; - } - } - | SECONDARY - { - if (state->mode == ARB_vertex) { - $$ = 1; - } else { - yyerror(& @1, state, "invalid program result name"); - YYERROR; - } - } - ; - -optFaceType: { $$ = 0; } - | FRONT { $$ = 0; } - | BACK { $$ = 1; } - ; - -optColorType: { $$ = 0; } - | PRIMARY { $$ = 0; } - | SECONDARY { $$ = 1; } - ; - -optTexCoordUnitNum: { $$ = 0; } - | '[' texCoordUnitNum ']' { $$ = $2; } - ; - -optTexImageUnitNum: { $$ = 0; } - | '[' texImageUnitNum ']' { $$ = $2; } - ; - -optLegacyTexUnitNum: { $$ = 0; } - | '[' legacyTexUnitNum ']' { $$ = $2; } - ; - -texCoordUnitNum: INTEGER - { - if ((unsigned) $1 >= state->MaxTextureCoordUnits) { - yyerror(& @1, state, "invalid texture coordinate unit selector"); - YYERROR; - } - - $$ = $1; - } - ; - -texImageUnitNum: INTEGER - { - if ((unsigned) $1 >= state->MaxTextureImageUnits) { - yyerror(& @1, state, "invalid texture image unit selector"); - YYERROR; - } - - $$ = $1; - } - ; - -legacyTexUnitNum: INTEGER - { - if ((unsigned) $1 >= state->MaxTextureUnits) { - yyerror(& @1, state, "invalid texture unit selector"); - YYERROR; - } - - $$ = $1; - } - ; - -ALIAS_statement: ALIAS IDENTIFIER '=' USED_IDENTIFIER - { - struct asm_symbol *exist = (struct asm_symbol *) - _mesa_symbol_table_find_symbol(state->st, 0, $2); - struct asm_symbol *target = (struct asm_symbol *) - _mesa_symbol_table_find_symbol(state->st, 0, $4); - - free($4); - - if (exist != NULL) { - char m[1000]; - _mesa_snprintf(m, sizeof(m), "redeclared identifier: %s", $2); - free($2); - yyerror(& @2, state, m); - YYERROR; - } else if (target == NULL) { - free($2); - yyerror(& @4, state, - "undefined variable binding in ALIAS statement"); - YYERROR; - } else { - _mesa_symbol_table_add_symbol(state->st, 0, $2, target); - } - } - ; - -string: IDENTIFIER - | USED_IDENTIFIER - ; - -%% - -void -asm_instruction_set_operands(struct asm_instruction *inst, - const struct prog_dst_register *dst, - const struct asm_src_register *src0, - const struct asm_src_register *src1, - const struct asm_src_register *src2) -{ - /* In the core ARB extensions only the KIL instruction doesn't have a - * destination register. - */ - if (dst == NULL) { - init_dst_reg(& inst->Base.DstReg); - } else { - inst->Base.DstReg = *dst; - } - - /* The only instruction that doesn't have any source registers is the - * condition-code based KIL instruction added by NV_fragment_program_option. - */ - if (src0 != NULL) { - inst->Base.SrcReg[0] = src0->Base; - inst->SrcReg[0] = *src0; - } else { - init_src_reg(& inst->SrcReg[0]); - } - - if (src1 != NULL) { - inst->Base.SrcReg[1] = src1->Base; - inst->SrcReg[1] = *src1; - } else { - init_src_reg(& inst->SrcReg[1]); - } - - if (src2 != NULL) { - inst->Base.SrcReg[2] = src2->Base; - inst->SrcReg[2] = *src2; - } else { - init_src_reg(& inst->SrcReg[2]); - } -} - - -struct asm_instruction * -asm_instruction_ctor(gl_inst_opcode op, - const struct prog_dst_register *dst, - const struct asm_src_register *src0, - const struct asm_src_register *src1, - const struct asm_src_register *src2) -{ - struct asm_instruction *inst = CALLOC_STRUCT(asm_instruction); - - if (inst) { - _mesa_init_instructions(& inst->Base, 1); - inst->Base.Opcode = op; - - asm_instruction_set_operands(inst, dst, src0, src1, src2); - } - - return inst; -} - - -struct asm_instruction * -asm_instruction_copy_ctor(const struct prog_instruction *base, - const struct prog_dst_register *dst, - const struct asm_src_register *src0, - const struct asm_src_register *src1, - const struct asm_src_register *src2) -{ - struct asm_instruction *inst = CALLOC_STRUCT(asm_instruction); - - if (inst) { - _mesa_init_instructions(& inst->Base, 1); - inst->Base.Opcode = base->Opcode; - inst->Base.CondUpdate = base->CondUpdate; - inst->Base.CondDst = base->CondDst; - inst->Base.SaturateMode = base->SaturateMode; - inst->Base.Precision = base->Precision; - - asm_instruction_set_operands(inst, dst, src0, src1, src2); - } - - return inst; -} - - -void -init_dst_reg(struct prog_dst_register *r) -{ - memset(r, 0, sizeof(*r)); - r->File = PROGRAM_UNDEFINED; - r->WriteMask = WRITEMASK_XYZW; - r->CondMask = COND_TR; - r->CondSwizzle = SWIZZLE_NOOP; -} - - -/** Like init_dst_reg() but set the File and Index fields. */ -void -set_dst_reg(struct prog_dst_register *r, gl_register_file file, GLint index) -{ - const GLint maxIndex = 1 << INST_INDEX_BITS; - const GLint minIndex = 0; - ASSERT(index >= minIndex); - (void) minIndex; - ASSERT(index <= maxIndex); - (void) maxIndex; - ASSERT(file == PROGRAM_TEMPORARY || - file == PROGRAM_ADDRESS || - file == PROGRAM_OUTPUT); - memset(r, 0, sizeof(*r)); - r->File = file; - r->Index = index; - r->WriteMask = WRITEMASK_XYZW; - r->CondMask = COND_TR; - r->CondSwizzle = SWIZZLE_NOOP; -} - - -void -init_src_reg(struct asm_src_register *r) -{ - memset(r, 0, sizeof(*r)); - r->Base.File = PROGRAM_UNDEFINED; - r->Base.Swizzle = SWIZZLE_NOOP; - r->Symbol = NULL; -} - - -/** Like init_src_reg() but set the File and Index fields. - * \return GL_TRUE if a valid src register, GL_FALSE otherwise - */ -void -set_src_reg(struct asm_src_register *r, gl_register_file file, GLint index) -{ - set_src_reg_swz(r, file, index, SWIZZLE_XYZW); -} - - -void -set_src_reg_swz(struct asm_src_register *r, gl_register_file file, GLint index, - GLuint swizzle) -{ - const GLint maxIndex = (1 << INST_INDEX_BITS) - 1; - const GLint minIndex = -(1 << INST_INDEX_BITS); - ASSERT(file < PROGRAM_FILE_MAX); - ASSERT(index >= minIndex); - (void) minIndex; - ASSERT(index <= maxIndex); - (void) maxIndex; - memset(r, 0, sizeof(*r)); - r->Base.File = file; - r->Base.Index = index; - r->Base.Swizzle = swizzle; - r->Symbol = NULL; -} - - -/** - * Validate the set of inputs used by a program - * - * Validates that legal sets of inputs are used by the program. In this case - * "used" included both reading the input or binding the input to a name using - * the \c ATTRIB command. - * - * \return - * \c TRUE if the combination of inputs used is valid, \c FALSE otherwise. - */ -int -validate_inputs(struct YYLTYPE *locp, struct asm_parser_state *state) -{ - const int inputs = state->prog->InputsRead | state->InputsBound; - - if (((inputs & 0x0ffff) & (inputs >> 16)) != 0) { - yyerror(locp, state, "illegal use of generic attribute and name attribute"); - return 0; - } - - return 1; -} - - -struct asm_symbol * -declare_variable(struct asm_parser_state *state, char *name, enum asm_type t, - struct YYLTYPE *locp) -{ - struct asm_symbol *s = NULL; - struct asm_symbol *exist = (struct asm_symbol *) - _mesa_symbol_table_find_symbol(state->st, 0, name); - - - if (exist != NULL) { - yyerror(locp, state, "redeclared identifier"); - } else { - s = calloc(1, sizeof(struct asm_symbol)); - s->name = name; - s->type = t; - - switch (t) { - case at_temp: - if (state->prog->NumTemporaries >= state->limits->MaxTemps) { - yyerror(locp, state, "too many temporaries declared"); - free(s); - return NULL; - } - - s->temp_binding = state->prog->NumTemporaries; - state->prog->NumTemporaries++; - break; - - case at_address: - if (state->prog->NumAddressRegs >= state->limits->MaxAddressRegs) { - yyerror(locp, state, "too many address registers declared"); - free(s); - return NULL; - } - - /* FINISHME: Add support for multiple address registers. - */ - state->prog->NumAddressRegs++; - break; - - default: - break; - } - - _mesa_symbol_table_add_symbol(state->st, 0, s->name, s); - s->next = state->sym; - state->sym = s; - } - - return s; -} - - -int add_state_reference(struct gl_program_parameter_list *param_list, - const gl_state_index tokens[STATE_LENGTH]) -{ - const GLuint size = 4; /* XXX fix */ - char *name; - GLint index; - - name = _mesa_program_state_string(tokens); - index = _mesa_add_parameter(param_list, PROGRAM_STATE_VAR, name, - size, GL_NONE, NULL, tokens, 0x0); - param_list->StateFlags |= _mesa_program_state_flags(tokens); - - /* free name string here since we duplicated it in add_parameter() */ - free(name); - - return index; -} - - -int -initialize_symbol_from_state(struct gl_program *prog, - struct asm_symbol *param_var, - const gl_state_index tokens[STATE_LENGTH]) -{ - int idx = -1; - gl_state_index state_tokens[STATE_LENGTH]; - - - memcpy(state_tokens, tokens, sizeof(state_tokens)); - - param_var->type = at_param; - param_var->param_binding_type = PROGRAM_STATE_VAR; - - /* If we are adding a STATE_MATRIX that has multiple rows, we need to - * unroll it and call add_state_reference() for each row - */ - if ((state_tokens[0] == STATE_MODELVIEW_MATRIX || - state_tokens[0] == STATE_PROJECTION_MATRIX || - state_tokens[0] == STATE_MVP_MATRIX || - state_tokens[0] == STATE_TEXTURE_MATRIX || - state_tokens[0] == STATE_PROGRAM_MATRIX) - && (state_tokens[2] != state_tokens[3])) { - int row; - const int first_row = state_tokens[2]; - const int last_row = state_tokens[3]; - - for (row = first_row; row <= last_row; row++) { - state_tokens[2] = state_tokens[3] = row; - - idx = add_state_reference(prog->Parameters, state_tokens); - if (param_var->param_binding_begin == ~0U) { - param_var->param_binding_begin = idx; - param_var->param_binding_swizzle = SWIZZLE_XYZW; - } - - param_var->param_binding_length++; - } - } - else { - idx = add_state_reference(prog->Parameters, state_tokens); - if (param_var->param_binding_begin == ~0U) { - param_var->param_binding_begin = idx; - param_var->param_binding_swizzle = SWIZZLE_XYZW; - } - param_var->param_binding_length++; - } - - return idx; -} - - -int -initialize_symbol_from_param(struct gl_program *prog, - struct asm_symbol *param_var, - const gl_state_index tokens[STATE_LENGTH]) -{ - int idx = -1; - gl_state_index state_tokens[STATE_LENGTH]; - - - memcpy(state_tokens, tokens, sizeof(state_tokens)); - - assert((state_tokens[0] == STATE_VERTEX_PROGRAM) - || (state_tokens[0] == STATE_FRAGMENT_PROGRAM)); - assert((state_tokens[1] == STATE_ENV) - || (state_tokens[1] == STATE_LOCAL)); - - /* - * The param type is STATE_VAR. The program parameter entry will - * effectively be a pointer into the LOCAL or ENV parameter array. - */ - param_var->type = at_param; - param_var->param_binding_type = PROGRAM_STATE_VAR; - - /* If we are adding a STATE_ENV or STATE_LOCAL that has multiple elements, - * we need to unroll it and call add_state_reference() for each row - */ - if (state_tokens[2] != state_tokens[3]) { - int row; - const int first_row = state_tokens[2]; - const int last_row = state_tokens[3]; - - for (row = first_row; row <= last_row; row++) { - state_tokens[2] = state_tokens[3] = row; - - idx = add_state_reference(prog->Parameters, state_tokens); - if (param_var->param_binding_begin == ~0U) { - param_var->param_binding_begin = idx; - param_var->param_binding_swizzle = SWIZZLE_XYZW; - } - param_var->param_binding_length++; - } - } - else { - idx = add_state_reference(prog->Parameters, state_tokens); - if (param_var->param_binding_begin == ~0U) { - param_var->param_binding_begin = idx; - param_var->param_binding_swizzle = SWIZZLE_XYZW; - } - param_var->param_binding_length++; - } - - return idx; -} - - -/** - * Put a float/vector constant/literal into the parameter list. - * \param param_var returns info about the parameter/constant's location, - * binding, type, etc. - * \param vec the vector/constant to add - * \param allowSwizzle if true, try to consolidate constants which only differ - * by a swizzle. We don't want to do this when building - * arrays of constants that may be indexed indirectly. - * \return index of the constant in the parameter list. - */ -int -initialize_symbol_from_const(struct gl_program *prog, - struct asm_symbol *param_var, - const struct asm_vector *vec, - GLboolean allowSwizzle) -{ - unsigned swizzle; - const int idx = _mesa_add_unnamed_constant(prog->Parameters, - vec->data, vec->count, - allowSwizzle ? &swizzle : NULL); - - param_var->type = at_param; - param_var->param_binding_type = PROGRAM_CONSTANT; - - if (param_var->param_binding_begin == ~0U) { - param_var->param_binding_begin = idx; - param_var->param_binding_swizzle = allowSwizzle ? swizzle : SWIZZLE_XYZW; - } - param_var->param_binding_length++; - - return idx; -} - - -char * -make_error_string(const char *fmt, ...) -{ - int length; - char *str; - va_list args; - - - /* Call vsnprintf once to determine how large the final string is. Call it - * again to do the actual formatting. from the vsnprintf manual page: - * - * Upon successful return, these functions return the number of - * characters printed (not including the trailing '\0' used to end - * output to strings). - */ - va_start(args, fmt); - length = 1 + vsnprintf(NULL, 0, fmt, args); - va_end(args); - - str = malloc(length); - if (str) { - va_start(args, fmt); - vsnprintf(str, length, fmt, args); - va_end(args); - } - - return str; -} - - -void -yyerror(YYLTYPE *locp, struct asm_parser_state *state, const char *s) -{ - char *err_str; - - - err_str = make_error_string("glProgramStringARB(%s)\n", s); - if (err_str) { - _mesa_error(state->ctx, GL_INVALID_OPERATION, "%s", err_str); - free(err_str); - } - - err_str = make_error_string("line %u, char %u: error: %s\n", - locp->first_line, locp->first_column, s); - _mesa_set_program_error(state->ctx, locp->position, err_str); - - if (err_str) { - free(err_str); - } -} - - -GLboolean -_mesa_parse_arb_program(struct gl_context *ctx, GLenum target, const GLubyte *str, - GLsizei len, struct asm_parser_state *state) -{ - struct asm_instruction *inst; - unsigned i; - GLubyte *strz; - GLboolean result = GL_FALSE; - void *temp; - struct asm_symbol *sym; - - state->ctx = ctx; - state->prog->Target = target; - state->prog->Parameters = _mesa_new_parameter_list(); - - /* Make a copy of the program string and force it to be NUL-terminated. - */ - strz = (GLubyte *) malloc(len + 1); - if (strz == NULL) { - _mesa_error(ctx, GL_OUT_OF_MEMORY, "glProgramStringARB"); - return GL_FALSE; - } - memcpy (strz, str, len); - strz[len] = '\0'; - - state->prog->String = strz; - - state->st = _mesa_symbol_table_ctor(); - - state->limits = (target == GL_VERTEX_PROGRAM_ARB) - ? & ctx->Const.VertexProgram - : & ctx->Const.FragmentProgram; - - state->MaxTextureImageUnits = ctx->Const.MaxTextureImageUnits; - state->MaxTextureCoordUnits = ctx->Const.MaxTextureCoordUnits; - state->MaxTextureUnits = ctx->Const.MaxTextureUnits; - state->MaxClipPlanes = ctx->Const.MaxClipPlanes; - state->MaxLights = ctx->Const.MaxLights; - state->MaxProgramMatrices = ctx->Const.MaxProgramMatrices; - state->MaxDrawBuffers = ctx->Const.MaxDrawBuffers; - - state->state_param_enum = (target == GL_VERTEX_PROGRAM_ARB) - ? STATE_VERTEX_PROGRAM : STATE_FRAGMENT_PROGRAM; - - _mesa_set_program_error(ctx, -1, NULL); - - _mesa_program_lexer_ctor(& state->scanner, state, (const char *) str, len); - yyparse(state); - _mesa_program_lexer_dtor(state->scanner); - - - if (ctx->Program.ErrorPos != -1) { - goto error; - } - - if (! _mesa_layout_parameters(state)) { - struct YYLTYPE loc; - - loc.first_line = 0; - loc.first_column = 0; - loc.position = len; - - yyerror(& loc, state, "invalid PARAM usage"); - goto error; - } - - - - /* Add one instruction to store the "END" instruction. - */ - state->prog->Instructions = - _mesa_alloc_instructions(state->prog->NumInstructions + 1); - inst = state->inst_head; - for (i = 0; i < state->prog->NumInstructions; i++) { - struct asm_instruction *const temp = inst->next; - - state->prog->Instructions[i] = inst->Base; - inst = temp; - } - - /* Finally, tag on an OPCODE_END instruction */ - { - const GLuint numInst = state->prog->NumInstructions; - _mesa_init_instructions(state->prog->Instructions + numInst, 1); - state->prog->Instructions[numInst].Opcode = OPCODE_END; - } - state->prog->NumInstructions++; - - state->prog->NumParameters = state->prog->Parameters->NumParameters; - state->prog->NumAttributes = _mesa_bitcount(state->prog->InputsRead); - - /* - * Initialize native counts to logical counts. The device driver may - * change them if program is translated into a hardware program. - */ - state->prog->NumNativeInstructions = state->prog->NumInstructions; - state->prog->NumNativeTemporaries = state->prog->NumTemporaries; - state->prog->NumNativeParameters = state->prog->NumParameters; - state->prog->NumNativeAttributes = state->prog->NumAttributes; - state->prog->NumNativeAddressRegs = state->prog->NumAddressRegs; - - result = GL_TRUE; - -error: - for (inst = state->inst_head; inst != NULL; inst = temp) { - temp = inst->next; - free(inst); - } - - state->inst_head = NULL; - state->inst_tail = NULL; - - for (sym = state->sym; sym != NULL; sym = temp) { - temp = sym->next; - - free((void *) sym->name); - free(sym); - } - state->sym = NULL; - - _mesa_symbol_table_dtor(state->st); - state->st = NULL; - - return result; -} +%{
+/*
+ * Copyright © 2009 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.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "main/mtypes.h"
+#include "main/imports.h"
+#include "program/program.h"
+#include "program/prog_parameter.h"
+#include "program/prog_parameter_layout.h"
+#include "program/prog_statevars.h"
+#include "program/prog_instruction.h"
+
+#include "program/symbol_table.h"
+#include "program/program_parser.h"
+
+extern void *yy_scan_string(char *);
+extern void yy_delete_buffer(void *);
+
+static struct asm_symbol *declare_variable(struct asm_parser_state *state,
+ char *name, enum asm_type t, struct YYLTYPE *locp);
+
+static int add_state_reference(struct gl_program_parameter_list *param_list,
+ const gl_state_index tokens[STATE_LENGTH]);
+
+static int initialize_symbol_from_state(struct gl_program *prog,
+ struct asm_symbol *param_var, const gl_state_index tokens[STATE_LENGTH]);
+
+static int initialize_symbol_from_param(struct gl_program *prog,
+ struct asm_symbol *param_var, const gl_state_index tokens[STATE_LENGTH]);
+
+static int initialize_symbol_from_const(struct gl_program *prog,
+ struct asm_symbol *param_var, const struct asm_vector *vec,
+ GLboolean allowSwizzle);
+
+static int yyparse(struct asm_parser_state *state);
+
+static char *make_error_string(const char *fmt, ...);
+
+static void yyerror(struct YYLTYPE *locp, struct asm_parser_state *state,
+ const char *s);
+
+static int validate_inputs(struct YYLTYPE *locp,
+ struct asm_parser_state *state);
+
+static void init_dst_reg(struct prog_dst_register *r);
+
+static void set_dst_reg(struct prog_dst_register *r,
+ gl_register_file file, GLint index);
+
+static void init_src_reg(struct asm_src_register *r);
+
+static void set_src_reg(struct asm_src_register *r,
+ gl_register_file file, GLint index);
+
+static void set_src_reg_swz(struct asm_src_register *r,
+ gl_register_file file, GLint index, GLuint swizzle);
+
+static void asm_instruction_set_operands(struct asm_instruction *inst,
+ const struct prog_dst_register *dst, const struct asm_src_register *src0,
+ const struct asm_src_register *src1, const struct asm_src_register *src2);
+
+static struct asm_instruction *asm_instruction_ctor(gl_inst_opcode op,
+ const struct prog_dst_register *dst, const struct asm_src_register *src0,
+ const struct asm_src_register *src1, const struct asm_src_register *src2);
+
+static struct asm_instruction *asm_instruction_copy_ctor(
+ const struct prog_instruction *base, const struct prog_dst_register *dst,
+ const struct asm_src_register *src0, const struct asm_src_register *src1,
+ const struct asm_src_register *src2);
+
+#ifndef FALSE
+#define FALSE 0
+#define TRUE (!FALSE)
+#endif
+
+#define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do { \
+ if (YYID(N)) { \
+ (Current).first_line = YYRHSLOC(Rhs, 1).first_line; \
+ (Current).first_column = YYRHSLOC(Rhs, 1).first_column; \
+ (Current).position = YYRHSLOC(Rhs, 1).position; \
+ (Current).last_line = YYRHSLOC(Rhs, N).last_line; \
+ (Current).last_column = YYRHSLOC(Rhs, N).last_column; \
+ } else { \
+ (Current).first_line = YYRHSLOC(Rhs, 0).last_line; \
+ (Current).last_line = (Current).first_line; \
+ (Current).first_column = YYRHSLOC(Rhs, 0).last_column; \
+ (Current).last_column = (Current).first_column; \
+ (Current).position = YYRHSLOC(Rhs, 0).position \
+ + (Current).first_column; \
+ } \
+ } while(YYID(0))
+
+#define YYLEX_PARAM state->scanner
+%}
+
+%pure-parser
+%locations
+%parse-param { struct asm_parser_state *state }
+%error-verbose
+%lex-param { void *scanner }
+
+%union {
+ struct asm_instruction *inst;
+ struct asm_symbol *sym;
+ struct asm_symbol temp_sym;
+ struct asm_swizzle_mask swiz_mask;
+ struct asm_src_register src_reg;
+ struct prog_dst_register dst_reg;
+ struct prog_instruction temp_inst;
+ char *string;
+ unsigned result;
+ unsigned attrib;
+ int integer;
+ float real;
+ gl_state_index state[STATE_LENGTH];
+ int negate;
+ struct asm_vector vector;
+ gl_inst_opcode opcode;
+
+ struct {
+ unsigned swz;
+ unsigned rgba_valid:1;
+ unsigned xyzw_valid:1;
+ unsigned negate:1;
+ } ext_swizzle;
+}
+
+%token ARBvp_10 ARBfp_10
+
+/* Tokens for assembler pseudo-ops */
+%token <integer> ADDRESS
+%token ALIAS ATTRIB
+%token OPTION OUTPUT
+%token PARAM
+%token <integer> TEMP
+%token END
+
+ /* Tokens for instructions */
+%token <temp_inst> BIN_OP BINSC_OP SAMPLE_OP SCALAR_OP TRI_OP VECTOR_OP
+%token <temp_inst> ARL KIL SWZ TXD_OP
+
+%token <integer> INTEGER
+%token <real> REAL
+
+%token AMBIENT ATTENUATION
+%token BACK
+%token CLIP COLOR
+%token DEPTH DIFFUSE DIRECTION
+%token EMISSION ENV EYE
+%token FOG FOGCOORD FRAGMENT FRONT
+%token HALF
+%token INVERSE INVTRANS
+%token LIGHT LIGHTMODEL LIGHTPROD LOCAL
+%token MATERIAL MAT_PROGRAM MATRIX MATRIXINDEX MODELVIEW MVP
+%token NORMAL
+%token OBJECT
+%token PALETTE PARAMS PLANE POINT_TOK POINTSIZE POSITION PRIMARY PROGRAM PROJECTION
+%token RANGE RESULT ROW
+%token SCENECOLOR SECONDARY SHININESS SIZE_TOK SPECULAR SPOT STATE
+%token TEXCOORD TEXENV TEXGEN TEXGEN_Q TEXGEN_R TEXGEN_S TEXGEN_T TEXTURE TRANSPOSE
+%token TEXTURE_UNIT TEX_1D TEX_2D TEX_3D TEX_CUBE TEX_RECT
+%token TEX_SHADOW1D TEX_SHADOW2D TEX_SHADOWRECT
+%token TEX_ARRAY1D TEX_ARRAY2D TEX_ARRAYSHADOW1D TEX_ARRAYSHADOW2D
+%token VERTEX VTXATTRIB
+%token WEIGHT
+
+%token <string> IDENTIFIER USED_IDENTIFIER
+%type <string> string
+%token <swiz_mask> MASK4 MASK3 MASK2 MASK1 SWIZZLE
+%token DOT_DOT
+%token DOT
+
+%type <inst> instruction ALU_instruction TexInstruction
+%type <inst> ARL_instruction VECTORop_instruction
+%type <inst> SCALARop_instruction BINSCop_instruction BINop_instruction
+%type <inst> TRIop_instruction TXD_instruction SWZ_instruction SAMPLE_instruction
+%type <inst> KIL_instruction
+
+%type <dst_reg> dstReg maskedDstReg maskedAddrReg
+%type <src_reg> srcReg scalarUse scalarSrcReg swizzleSrcReg
+%type <swiz_mask> scalarSuffix swizzleSuffix extendedSwizzle
+%type <ext_swizzle> extSwizComp extSwizSel
+%type <swiz_mask> optionalMask
+
+%type <sym> progParamArray
+%type <integer> addrRegRelOffset addrRegPosOffset addrRegNegOffset
+%type <src_reg> progParamArrayMem progParamArrayAbs progParamArrayRel
+%type <sym> addrReg
+%type <swiz_mask> addrComponent addrWriteMask
+
+%type <dst_reg> ccMaskRule ccTest ccMaskRule2 ccTest2 optionalCcMask
+
+%type <result> resultBinding resultColBinding
+%type <integer> optFaceType optColorType
+%type <integer> optResultFaceType optResultColorType
+
+%type <integer> optTexImageUnitNum texImageUnitNum
+%type <integer> optTexCoordUnitNum texCoordUnitNum
+%type <integer> optLegacyTexUnitNum legacyTexUnitNum
+%type <integer> texImageUnit texTarget
+%type <integer> vtxAttribNum
+
+%type <attrib> attribBinding vtxAttribItem fragAttribItem
+
+%type <temp_sym> paramSingleInit paramSingleItemDecl
+%type <integer> optArraySize
+
+%type <state> stateSingleItem stateMultipleItem
+%type <state> stateMaterialItem
+%type <state> stateLightItem stateLightModelItem stateLightProdItem
+%type <state> stateTexGenItem stateFogItem stateClipPlaneItem statePointItem
+%type <state> stateMatrixItem stateMatrixRow stateMatrixRows
+%type <state> stateTexEnvItem stateDepthItem
+
+%type <state> stateLModProperty
+%type <state> stateMatrixName optMatrixRows
+
+%type <integer> stateMatProperty
+%type <integer> stateLightProperty stateSpotProperty
+%type <integer> stateLightNumber stateLProdProperty
+%type <integer> stateTexGenType stateTexGenCoord
+%type <integer> stateTexEnvProperty
+%type <integer> stateFogProperty
+%type <integer> stateClipPlaneNum
+%type <integer> statePointProperty
+
+%type <integer> stateOptMatModifier stateMatModifier stateMatrixRowNum
+%type <integer> stateOptModMatNum stateModMatNum statePaletteMatNum
+%type <integer> stateProgramMatNum
+
+%type <integer> ambDiffSpecProperty
+
+%type <state> programSingleItem progEnvParam progLocalParam
+%type <state> programMultipleItem progEnvParams progLocalParams
+
+%type <temp_sym> paramMultipleInit paramMultInitList paramMultipleItem
+%type <temp_sym> paramSingleItemUse
+
+%type <integer> progEnvParamNum progLocalParamNum
+%type <state> progEnvParamNums progLocalParamNums
+
+%type <vector> paramConstDecl paramConstUse
+%type <vector> paramConstScalarDecl paramConstScalarUse paramConstVector
+%type <real> signedFloatConstant
+%type <negate> optionalSign
+
+%{
+extern int yylex(YYSTYPE *yylval_param, YYLTYPE *yylloc_param,
+ void *yyscanner);
+%}
+
+%%
+
+program: language optionSequence statementSequence END
+ ;
+
+language: ARBvp_10
+ {
+ if (state->prog->Target != GL_VERTEX_PROGRAM_ARB) {
+ yyerror(& @1, state, "invalid fragment program header");
+
+ }
+ state->mode = ARB_vertex;
+ }
+ | ARBfp_10
+ {
+ if (state->prog->Target != GL_FRAGMENT_PROGRAM_ARB) {
+ yyerror(& @1, state, "invalid vertex program header");
+ }
+ state->mode = ARB_fragment;
+
+ state->option.TexRect =
+ (state->ctx->Extensions.NV_texture_rectangle != GL_FALSE);
+ }
+ ;
+
+optionSequence: optionSequence option
+ |
+ ;
+
+option: OPTION string ';'
+ {
+ int valid = 0;
+
+ if (state->mode == ARB_vertex) {
+ valid = _mesa_ARBvp_parse_option(state, $2);
+ } else if (state->mode == ARB_fragment) {
+ valid = _mesa_ARBfp_parse_option(state, $2);
+ }
+
+
+ free($2);
+
+ if (!valid) {
+ const char *const err_str = (state->mode == ARB_vertex)
+ ? "invalid ARB vertex program option"
+ : "invalid ARB fragment program option";
+
+ yyerror(& @2, state, err_str);
+ YYERROR;
+ }
+ }
+ ;
+
+statementSequence: statementSequence statement
+ |
+ ;
+
+statement: instruction ';'
+ {
+ if ($1 != NULL) {
+ if (state->inst_tail == NULL) {
+ state->inst_head = $1;
+ } else {
+ state->inst_tail->next = $1;
+ }
+
+ state->inst_tail = $1;
+ $1->next = NULL;
+
+ state->prog->NumInstructions++;
+ }
+ }
+ | namingStatement ';'
+ ;
+
+instruction: ALU_instruction
+ {
+ $$ = $1;
+ state->prog->NumAluInstructions++;
+ }
+ | TexInstruction
+ {
+ $$ = $1;
+ state->prog->NumTexInstructions++;
+ }
+ ;
+
+ALU_instruction: ARL_instruction
+ | VECTORop_instruction
+ | SCALARop_instruction
+ | BINSCop_instruction
+ | BINop_instruction
+ | TRIop_instruction
+ | SWZ_instruction
+ ;
+
+TexInstruction: SAMPLE_instruction
+ | KIL_instruction
+ | TXD_instruction
+ ;
+
+ARL_instruction: ARL maskedAddrReg ',' scalarSrcReg
+ {
+ $$ = asm_instruction_ctor(OPCODE_ARL, & $2, & $4, NULL, NULL);
+ }
+ ;
+
+VECTORop_instruction: VECTOR_OP maskedDstReg ',' swizzleSrcReg
+ {
+ $$ = asm_instruction_copy_ctor(& $1, & $2, & $4, NULL, NULL);
+ }
+ ;
+
+SCALARop_instruction: SCALAR_OP maskedDstReg ',' scalarSrcReg
+ {
+ $$ = asm_instruction_copy_ctor(& $1, & $2, & $4, NULL, NULL);
+ }
+ ;
+
+BINSCop_instruction: BINSC_OP maskedDstReg ',' scalarSrcReg ',' scalarSrcReg
+ {
+ $$ = asm_instruction_copy_ctor(& $1, & $2, & $4, & $6, NULL);
+ }
+ ;
+
+
+BINop_instruction: BIN_OP maskedDstReg ',' swizzleSrcReg ',' swizzleSrcReg
+ {
+ $$ = asm_instruction_copy_ctor(& $1, & $2, & $4, & $6, NULL);
+ }
+ ;
+
+TRIop_instruction: TRI_OP maskedDstReg ','
+ swizzleSrcReg ',' swizzleSrcReg ',' swizzleSrcReg
+ {
+ $$ = asm_instruction_copy_ctor(& $1, & $2, & $4, & $6, & $8);
+ }
+ ;
+
+SAMPLE_instruction: SAMPLE_OP maskedDstReg ',' swizzleSrcReg ',' texImageUnit ',' texTarget
+ {
+ $$ = asm_instruction_copy_ctor(& $1, & $2, & $4, NULL, NULL);
+ if ($$ != NULL) {
+ const GLbitfield tex_mask = (1U << $6);
+ GLbitfield shadow_tex = 0;
+ GLbitfield target_mask = 0;
+
+
+ $$->Base.TexSrcUnit = $6;
+
+ if ($8 < 0) {
+ shadow_tex = tex_mask;
+
+ $$->Base.TexSrcTarget = -$8;
+ $$->Base.TexShadow = 1;
+ } else {
+ $$->Base.TexSrcTarget = $8;
+ }
+
+ target_mask = (1U << $$->Base.TexSrcTarget);
+
+ /* If this texture unit was previously accessed and that access
+ * had a different texture target, generate an error.
+ *
+ * If this texture unit was previously accessed and that access
+ * had a different shadow mode, generate an error.
+ */
+ if ((state->prog->TexturesUsed[$6] != 0)
+ && ((state->prog->TexturesUsed[$6] != target_mask)
+ || ((state->prog->ShadowSamplers & tex_mask)
+ != shadow_tex))) {
+ yyerror(& @8, state,
+ "multiple targets used on one texture image unit");
+ YYERROR;
+ }
+
+
+ state->prog->TexturesUsed[$6] |= target_mask;
+ state->prog->ShadowSamplers |= shadow_tex;
+ }
+ }
+ ;
+
+KIL_instruction: KIL swizzleSrcReg
+ {
+ $$ = asm_instruction_ctor(OPCODE_KIL, NULL, & $2, NULL, NULL);
+ state->fragment.UsesKill = 1;
+ }
+ | KIL ccTest
+ {
+ $$ = asm_instruction_ctor(OPCODE_KIL_NV, NULL, NULL, NULL, NULL);
+ $$->Base.DstReg.CondMask = $2.CondMask;
+ $$->Base.DstReg.CondSwizzle = $2.CondSwizzle;
+ $$->Base.DstReg.CondSrc = $2.CondSrc;
+ state->fragment.UsesKill = 1;
+ }
+ ;
+
+TXD_instruction: TXD_OP maskedDstReg ',' swizzleSrcReg ',' swizzleSrcReg ',' swizzleSrcReg ',' texImageUnit ',' texTarget
+ {
+ $$ = asm_instruction_copy_ctor(& $1, & $2, & $4, & $6, & $8);
+ if ($$ != NULL) {
+ const GLbitfield tex_mask = (1U << $10);
+ GLbitfield shadow_tex = 0;
+ GLbitfield target_mask = 0;
+
+
+ $$->Base.TexSrcUnit = $10;
+
+ if ($12 < 0) {
+ shadow_tex = tex_mask;
+
+ $$->Base.TexSrcTarget = -$12;
+ $$->Base.TexShadow = 1;
+ } else {
+ $$->Base.TexSrcTarget = $12;
+ }
+
+ target_mask = (1U << $$->Base.TexSrcTarget);
+
+ /* If this texture unit was previously accessed and that access
+ * had a different texture target, generate an error.
+ *
+ * If this texture unit was previously accessed and that access
+ * had a different shadow mode, generate an error.
+ */
+ if ((state->prog->TexturesUsed[$10] != 0)
+ && ((state->prog->TexturesUsed[$10] != target_mask)
+ || ((state->prog->ShadowSamplers & tex_mask)
+ != shadow_tex))) {
+ yyerror(& @12, state,
+ "multiple targets used on one texture image unit");
+ YYERROR;
+ }
+
+
+ state->prog->TexturesUsed[$10] |= target_mask;
+ state->prog->ShadowSamplers |= shadow_tex;
+ }
+ }
+ ;
+
+texImageUnit: TEXTURE_UNIT optTexImageUnitNum
+ {
+ $$ = $2;
+ }
+ ;
+
+texTarget: TEX_1D { $$ = TEXTURE_1D_INDEX; }
+ | TEX_2D { $$ = TEXTURE_2D_INDEX; }
+ | TEX_3D { $$ = TEXTURE_3D_INDEX; }
+ | TEX_CUBE { $$ = TEXTURE_CUBE_INDEX; }
+ | TEX_RECT { $$ = TEXTURE_RECT_INDEX; }
+ | TEX_SHADOW1D { $$ = -TEXTURE_1D_INDEX; }
+ | TEX_SHADOW2D { $$ = -TEXTURE_2D_INDEX; }
+ | TEX_SHADOWRECT { $$ = -TEXTURE_RECT_INDEX; }
+ | TEX_ARRAY1D { $$ = TEXTURE_1D_ARRAY_INDEX; }
+ | TEX_ARRAY2D { $$ = TEXTURE_2D_ARRAY_INDEX; }
+ | TEX_ARRAYSHADOW1D { $$ = -TEXTURE_1D_ARRAY_INDEX; }
+ | TEX_ARRAYSHADOW2D { $$ = -TEXTURE_2D_ARRAY_INDEX; }
+ ;
+
+SWZ_instruction: SWZ maskedDstReg ',' srcReg ',' extendedSwizzle
+ {
+ /* FIXME: Is this correct? Should the extenedSwizzle be applied
+ * FIXME: to the existing swizzle?
+ */
+ $4.Base.Swizzle = $6.swizzle;
+ $4.Base.Negate = $6.mask;
+
+ $$ = asm_instruction_copy_ctor(& $1, & $2, & $4, NULL, NULL);
+ }
+ ;
+
+scalarSrcReg: optionalSign scalarUse
+ {
+ $$ = $2;
+
+ if ($1) {
+ $$.Base.Negate = ~$$.Base.Negate;
+ }
+ }
+ | optionalSign '|' scalarUse '|'
+ {
+ $$ = $3;
+
+ if (!state->option.NV_fragment) {
+ yyerror(& @2, state, "unexpected character '|'");
+ YYERROR;
+ }
+
+ if ($1) {
+ $$.Base.Negate = ~$$.Base.Negate;
+ }
+
+ $$.Base.Abs = 1;
+ }
+ ;
+
+scalarUse: srcReg scalarSuffix
+ {
+ $$ = $1;
+
+ $$.Base.Swizzle = _mesa_combine_swizzles($$.Base.Swizzle,
+ $2.swizzle);
+ }
+ | paramConstScalarUse
+ {
+ struct asm_symbol temp_sym;
+
+ if (!state->option.NV_fragment) {
+ yyerror(& @1, state, "expected scalar suffix");
+ YYERROR;
+ }
+
+ memset(& temp_sym, 0, sizeof(temp_sym));
+ temp_sym.param_binding_begin = ~0;
+ initialize_symbol_from_const(state->prog, & temp_sym, & $1, GL_TRUE);
+
+ set_src_reg_swz(& $$, PROGRAM_CONSTANT,
+ temp_sym.param_binding_begin,
+ temp_sym.param_binding_swizzle);
+ }
+ ;
+
+swizzleSrcReg: optionalSign srcReg swizzleSuffix
+ {
+ $$ = $2;
+
+ if ($1) {
+ $$.Base.Negate = ~$$.Base.Negate;
+ }
+
+ $$.Base.Swizzle = _mesa_combine_swizzles($$.Base.Swizzle,
+ $3.swizzle);
+ }
+ | optionalSign '|' srcReg swizzleSuffix '|'
+ {
+ $$ = $3;
+
+ if (!state->option.NV_fragment) {
+ yyerror(& @2, state, "unexpected character '|'");
+ YYERROR;
+ }
+
+ if ($1) {
+ $$.Base.Negate = ~$$.Base.Negate;
+ }
+
+ $$.Base.Abs = 1;
+ $$.Base.Swizzle = _mesa_combine_swizzles($$.Base.Swizzle,
+ $4.swizzle);
+ }
+
+ ;
+
+maskedDstReg: dstReg optionalMask optionalCcMask
+ {
+ $$ = $1;
+ $$.WriteMask = $2.mask;
+ $$.CondMask = $3.CondMask;
+ $$.CondSwizzle = $3.CondSwizzle;
+ $$.CondSrc = $3.CondSrc;
+
+ if ($$.File == PROGRAM_OUTPUT) {
+ /* Technically speaking, this should check that it is in
+ * vertex program mode. However, PositionInvariant can never be
+ * set in fragment program mode, so it is somewhat irrelevant.
+ */
+ if (state->option.PositionInvariant
+ && ($$.Index == VERT_RESULT_HPOS)) {
+ yyerror(& @1, state, "position-invariant programs cannot "
+ "write position");
+ YYERROR;
+ }
+
+ state->prog->OutputsWritten |= BITFIELD64_BIT($$.Index);
+ }
+ }
+ ;
+
+maskedAddrReg: addrReg addrWriteMask
+ {
+ set_dst_reg(& $$, PROGRAM_ADDRESS, 0);
+ $$.WriteMask = $2.mask;
+ }
+ ;
+
+extendedSwizzle: extSwizComp ',' extSwizComp ',' extSwizComp ',' extSwizComp
+ {
+ const unsigned xyzw_valid =
+ ($1.xyzw_valid << 0)
+ | ($3.xyzw_valid << 1)
+ | ($5.xyzw_valid << 2)
+ | ($7.xyzw_valid << 3);
+ const unsigned rgba_valid =
+ ($1.rgba_valid << 0)
+ | ($3.rgba_valid << 1)
+ | ($5.rgba_valid << 2)
+ | ($7.rgba_valid << 3);
+
+ /* All of the swizzle components have to be valid in either RGBA
+ * or XYZW. Note that 0 and 1 are valid in both, so both masks
+ * can have some bits set.
+ *
+ * We somewhat deviate from the spec here. It would be really hard
+ * to figure out which component is the error, and there probably
+ * isn't a lot of benefit.
+ */
+ if ((rgba_valid != 0x0f) && (xyzw_valid != 0x0f)) {
+ yyerror(& @1, state, "cannot combine RGBA and XYZW swizzle "
+ "components");
+ YYERROR;
+ }
+
+ $$.swizzle = MAKE_SWIZZLE4($1.swz, $3.swz, $5.swz, $7.swz);
+ $$.mask = ($1.negate) | ($3.negate << 1) | ($5.negate << 2)
+ | ($7.negate << 3);
+ }
+ ;
+
+extSwizComp: optionalSign extSwizSel
+ {
+ $$ = $2;
+ $$.negate = ($1) ? 1 : 0;
+ }
+ ;
+
+extSwizSel: INTEGER
+ {
+ if (($1 != 0) && ($1 != 1)) {
+ yyerror(& @1, state, "invalid extended swizzle selector");
+ YYERROR;
+ }
+
+ $$.swz = ($1 == 0) ? SWIZZLE_ZERO : SWIZZLE_ONE;
+
+ /* 0 and 1 are valid for both RGBA swizzle names and XYZW
+ * swizzle names.
+ */
+ $$.xyzw_valid = 1;
+ $$.rgba_valid = 1;
+ }
+ | string
+ {
+ char s;
+
+ if (strlen($1) > 1) {
+ yyerror(& @1, state, "invalid extended swizzle selector");
+ YYERROR;
+ }
+
+ s = $1[0];
+ free($1);
+
+ switch (s) {
+ case 'x':
+ $$.swz = SWIZZLE_X;
+ $$.xyzw_valid = 1;
+ break;
+ case 'y':
+ $$.swz = SWIZZLE_Y;
+ $$.xyzw_valid = 1;
+ break;
+ case 'z':
+ $$.swz = SWIZZLE_Z;
+ $$.xyzw_valid = 1;
+ break;
+ case 'w':
+ $$.swz = SWIZZLE_W;
+ $$.xyzw_valid = 1;
+ break;
+
+ case 'r':
+ $$.swz = SWIZZLE_X;
+ $$.rgba_valid = 1;
+ break;
+ case 'g':
+ $$.swz = SWIZZLE_Y;
+ $$.rgba_valid = 1;
+ break;
+ case 'b':
+ $$.swz = SWIZZLE_Z;
+ $$.rgba_valid = 1;
+ break;
+ case 'a':
+ $$.swz = SWIZZLE_W;
+ $$.rgba_valid = 1;
+ break;
+
+ default:
+ yyerror(& @1, state, "invalid extended swizzle selector");
+ YYERROR;
+ break;
+ }
+ }
+ ;
+
+srcReg: USED_IDENTIFIER /* temporaryReg | progParamSingle */
+ {
+ struct asm_symbol *const s = (struct asm_symbol *)
+ _mesa_symbol_table_find_symbol(state->st, 0, $1);
+
+ free($1);
+
+ if (s == NULL) {
+ yyerror(& @1, state, "invalid operand variable");
+ YYERROR;
+ } else if ((s->type != at_param) && (s->type != at_temp)
+ && (s->type != at_attrib)) {
+ yyerror(& @1, state, "invalid operand variable");
+ YYERROR;
+ } else if ((s->type == at_param) && s->param_is_array) {
+ yyerror(& @1, state, "non-array access to array PARAM");
+ YYERROR;
+ }
+
+ init_src_reg(& $$);
+ switch (s->type) {
+ case at_temp:
+ set_src_reg(& $$, PROGRAM_TEMPORARY, s->temp_binding);
+ break;
+ case at_param:
+ set_src_reg_swz(& $$, s->param_binding_type,
+ s->param_binding_begin,
+ s->param_binding_swizzle);
+ break;
+ case at_attrib:
+ set_src_reg(& $$, PROGRAM_INPUT, s->attrib_binding);
+ state->prog->InputsRead |= (1U << $$.Base.Index);
+
+ if (!validate_inputs(& @1, state)) {
+ YYERROR;
+ }
+ break;
+
+ default:
+ YYERROR;
+ break;
+ }
+ }
+ | attribBinding
+ {
+ set_src_reg(& $$, PROGRAM_INPUT, $1);
+ state->prog->InputsRead |= (1U << $$.Base.Index);
+
+ if (!validate_inputs(& @1, state)) {
+ YYERROR;
+ }
+ }
+ | progParamArray '[' progParamArrayMem ']'
+ {
+ if (! $3.Base.RelAddr
+ && ((unsigned) $3.Base.Index >= $1->param_binding_length)) {
+ yyerror(& @3, state, "out of bounds array access");
+ YYERROR;
+ }
+
+ init_src_reg(& $$);
+ $$.Base.File = $1->param_binding_type;
+
+ if ($3.Base.RelAddr) {
+ state->prog->IndirectRegisterFiles |= (1 << $$.Base.File);
+ $1->param_accessed_indirectly = 1;
+
+ $$.Base.RelAddr = 1;
+ $$.Base.Index = $3.Base.Index;
+ $$.Symbol = $1;
+ } else {
+ $$.Base.Index = $1->param_binding_begin + $3.Base.Index;
+ }
+ }
+ | paramSingleItemUse
+ {
+ gl_register_file file = ($1.name != NULL)
+ ? $1.param_binding_type
+ : PROGRAM_CONSTANT;
+ set_src_reg_swz(& $$, file, $1.param_binding_begin,
+ $1.param_binding_swizzle);
+ }
+ ;
+
+dstReg: resultBinding
+ {
+ set_dst_reg(& $$, PROGRAM_OUTPUT, $1);
+ }
+ | USED_IDENTIFIER /* temporaryReg | vertexResultReg */
+ {
+ struct asm_symbol *const s = (struct asm_symbol *)
+ _mesa_symbol_table_find_symbol(state->st, 0, $1);
+
+ free($1);
+
+ if (s == NULL) {
+ yyerror(& @1, state, "invalid operand variable");
+ YYERROR;
+ } else if ((s->type != at_output) && (s->type != at_temp)) {
+ yyerror(& @1, state, "invalid operand variable");
+ YYERROR;
+ }
+
+ switch (s->type) {
+ case at_temp:
+ set_dst_reg(& $$, PROGRAM_TEMPORARY, s->temp_binding);
+ break;
+ case at_output:
+ set_dst_reg(& $$, PROGRAM_OUTPUT, s->output_binding);
+ break;
+ default:
+ set_dst_reg(& $$, s->param_binding_type, s->param_binding_begin);
+ break;
+ }
+ }
+ ;
+
+progParamArray: USED_IDENTIFIER
+ {
+ struct asm_symbol *const s = (struct asm_symbol *)
+ _mesa_symbol_table_find_symbol(state->st, 0, $1);
+
+ free($1);
+
+ if (s == NULL) {
+ yyerror(& @1, state, "invalid operand variable");
+ YYERROR;
+ } else if ((s->type != at_param) || !s->param_is_array) {
+ yyerror(& @1, state, "array access to non-PARAM variable");
+ YYERROR;
+ } else {
+ $$ = s;
+ }
+ }
+ ;
+
+progParamArrayMem: progParamArrayAbs | progParamArrayRel;
+
+progParamArrayAbs: INTEGER
+ {
+ init_src_reg(& $$);
+ $$.Base.Index = $1;
+ }
+ ;
+
+progParamArrayRel: addrReg addrComponent addrRegRelOffset
+ {
+ /* FINISHME: Add support for multiple address registers.
+ */
+ /* FINISHME: Add support for 4-component address registers.
+ */
+ init_src_reg(& $$);
+ $$.Base.RelAddr = 1;
+ $$.Base.Index = $3;
+ }
+ ;
+
+addrRegRelOffset: { $$ = 0; }
+ | '+' addrRegPosOffset { $$ = $2; }
+ | '-' addrRegNegOffset { $$ = -$2; }
+ ;
+
+addrRegPosOffset: INTEGER
+ {
+ if (($1 < 0) || ($1 > (state->limits->MaxAddressOffset - 1))) {
+ char s[100];
+ _mesa_snprintf(s, sizeof(s),
+ "relative address offset too large (%d)", $1);
+ yyerror(& @1, state, s);
+ YYERROR;
+ } else {
+ $$ = $1;
+ }
+ }
+ ;
+
+addrRegNegOffset: INTEGER
+ {
+ if (($1 < 0) || ($1 > state->limits->MaxAddressOffset)) {
+ char s[100];
+ _mesa_snprintf(s, sizeof(s),
+ "relative address offset too large (%d)", $1);
+ yyerror(& @1, state, s);
+ YYERROR;
+ } else {
+ $$ = $1;
+ }
+ }
+ ;
+
+addrReg: USED_IDENTIFIER
+ {
+ struct asm_symbol *const s = (struct asm_symbol *)
+ _mesa_symbol_table_find_symbol(state->st, 0, $1);
+
+ free($1);
+
+ if (s == NULL) {
+ yyerror(& @1, state, "invalid array member");
+ YYERROR;
+ } else if (s->type != at_address) {
+ yyerror(& @1, state,
+ "invalid variable for indexed array access");
+ YYERROR;
+ } else {
+ $$ = s;
+ }
+ }
+ ;
+
+addrComponent: MASK1
+ {
+ if ($1.mask != WRITEMASK_X) {
+ yyerror(& @1, state, "invalid address component selector");
+ YYERROR;
+ } else {
+ $$ = $1;
+ }
+ }
+ ;
+
+addrWriteMask: MASK1
+ {
+ if ($1.mask != WRITEMASK_X) {
+ yyerror(& @1, state,
+ "address register write mask must be \".x\"");
+ YYERROR;
+ } else {
+ $$ = $1;
+ }
+ }
+ ;
+
+scalarSuffix: MASK1;
+
+swizzleSuffix: MASK1
+ | MASK4
+ | SWIZZLE
+ | { $$.swizzle = SWIZZLE_NOOP; $$.mask = WRITEMASK_XYZW; }
+ ;
+
+optionalMask: MASK4 | MASK3 | MASK2 | MASK1
+ | { $$.swizzle = SWIZZLE_NOOP; $$.mask = WRITEMASK_XYZW; }
+ ;
+
+optionalCcMask: '(' ccTest ')'
+ {
+ $$ = $2;
+ }
+ | '(' ccTest2 ')'
+ {
+ $$ = $2;
+ }
+ |
+ {
+ $$.CondMask = COND_TR;
+ $$.CondSwizzle = SWIZZLE_NOOP;
+ $$.CondSrc = 0;
+ }
+ ;
+
+ccTest: ccMaskRule swizzleSuffix
+ {
+ $$ = $1;
+ $$.CondSwizzle = $2.swizzle;
+ }
+ ;
+
+ccTest2: ccMaskRule2 swizzleSuffix
+ {
+ $$ = $1;
+ $$.CondSwizzle = $2.swizzle;
+ }
+ ;
+
+ccMaskRule: IDENTIFIER
+ {
+ const int cond = _mesa_parse_cc($1);
+ if ((cond == 0) || ($1[2] != '\0')) {
+ char *const err_str =
+ make_error_string("invalid condition code \"%s\"", $1);
+
+ yyerror(& @1, state, (err_str != NULL)
+ ? err_str : "invalid condition code");
+
+ if (err_str != NULL) {
+ free(err_str);
+ }
+
+ YYERROR;
+ }
+
+ $$.CondMask = cond;
+ $$.CondSwizzle = SWIZZLE_NOOP;
+ $$.CondSrc = 0;
+ }
+ ;
+
+ccMaskRule2: USED_IDENTIFIER
+ {
+ const int cond = _mesa_parse_cc($1);
+ if ((cond == 0) || ($1[2] != '\0')) {
+ char *const err_str =
+ make_error_string("invalid condition code \"%s\"", $1);
+
+ yyerror(& @1, state, (err_str != NULL)
+ ? err_str : "invalid condition code");
+
+ if (err_str != NULL) {
+ free(err_str);
+ }
+
+ YYERROR;
+ }
+
+ $$.CondMask = cond;
+ $$.CondSwizzle = SWIZZLE_NOOP;
+ $$.CondSrc = 0;
+ }
+ ;
+
+namingStatement: ATTRIB_statement
+ | PARAM_statement
+ | TEMP_statement
+ | ADDRESS_statement
+ | OUTPUT_statement
+ | ALIAS_statement
+ ;
+
+ATTRIB_statement: ATTRIB IDENTIFIER '=' attribBinding
+ {
+ struct asm_symbol *const s =
+ declare_variable(state, $2, at_attrib, & @2);
+
+ if (s == NULL) {
+ free($2);
+ YYERROR;
+ } else {
+ s->attrib_binding = $4;
+ state->InputsBound |= (1U << s->attrib_binding);
+
+ if (!validate_inputs(& @4, state)) {
+ YYERROR;
+ }
+ }
+ }
+ ;
+
+attribBinding: VERTEX vtxAttribItem
+ {
+ $$ = $2;
+ }
+ | FRAGMENT fragAttribItem
+ {
+ $$ = $2;
+ }
+ ;
+
+vtxAttribItem: POSITION
+ {
+ $$ = VERT_ATTRIB_POS;
+ }
+ | WEIGHT vtxOptWeightNum
+ {
+ $$ = VERT_ATTRIB_WEIGHT;
+ }
+ | NORMAL
+ {
+ $$ = VERT_ATTRIB_NORMAL;
+ }
+ | COLOR optColorType
+ {
+ if (!state->ctx->Extensions.EXT_secondary_color) {
+ yyerror(& @2, state, "GL_EXT_secondary_color not supported");
+ YYERROR;
+ }
+
+ $$ = VERT_ATTRIB_COLOR0 + $2;
+ }
+ | FOGCOORD
+ {
+ if (!state->ctx->Extensions.EXT_fog_coord) {
+ yyerror(& @1, state, "GL_EXT_fog_coord not supported");
+ YYERROR;
+ }
+
+ $$ = VERT_ATTRIB_FOG;
+ }
+ | TEXCOORD optTexCoordUnitNum
+ {
+ $$ = VERT_ATTRIB_TEX0 + $2;
+ }
+ | MATRIXINDEX '[' vtxWeightNum ']'
+ {
+ yyerror(& @1, state, "GL_ARB_matrix_palette not supported");
+ YYERROR;
+ }
+ | VTXATTRIB '[' vtxAttribNum ']'
+ {
+ $$ = VERT_ATTRIB_GENERIC0 + $3;
+ }
+ ;
+
+vtxAttribNum: INTEGER
+ {
+ if ((unsigned) $1 >= state->limits->MaxAttribs) {
+ yyerror(& @1, state, "invalid vertex attribute reference");
+ YYERROR;
+ }
+
+ $$ = $1;
+ }
+ ;
+
+vtxOptWeightNum: | '[' vtxWeightNum ']';
+vtxWeightNum: INTEGER;
+
+fragAttribItem: POSITION
+ {
+ $$ = FRAG_ATTRIB_WPOS;
+ }
+ | COLOR optColorType
+ {
+ $$ = FRAG_ATTRIB_COL0 + $2;
+ }
+ | FOGCOORD
+ {
+ $$ = FRAG_ATTRIB_FOGC;
+ }
+ | TEXCOORD optTexCoordUnitNum
+ {
+ $$ = FRAG_ATTRIB_TEX0 + $2;
+ }
+ ;
+
+PARAM_statement: PARAM_singleStmt | PARAM_multipleStmt;
+
+PARAM_singleStmt: PARAM IDENTIFIER paramSingleInit
+ {
+ struct asm_symbol *const s =
+ declare_variable(state, $2, at_param, & @2);
+
+ if (s == NULL) {
+ free($2);
+ YYERROR;
+ } else {
+ s->param_binding_type = $3.param_binding_type;
+ s->param_binding_begin = $3.param_binding_begin;
+ s->param_binding_length = $3.param_binding_length;
+ s->param_binding_swizzle = $3.param_binding_swizzle;
+ s->param_is_array = 0;
+ }
+ }
+ ;
+
+PARAM_multipleStmt: PARAM IDENTIFIER '[' optArraySize ']' paramMultipleInit
+ {
+ if (($4 != 0) && ((unsigned) $4 != $6.param_binding_length)) {
+ free($2);
+ yyerror(& @4, state,
+ "parameter array size and number of bindings must match");
+ YYERROR;
+ } else {
+ struct asm_symbol *const s =
+ declare_variable(state, $2, $6.type, & @2);
+
+ if (s == NULL) {
+ free($2);
+ YYERROR;
+ } else {
+ s->param_binding_type = $6.param_binding_type;
+ s->param_binding_begin = $6.param_binding_begin;
+ s->param_binding_length = $6.param_binding_length;
+ s->param_binding_swizzle = SWIZZLE_XYZW;
+ s->param_is_array = 1;
+ }
+ }
+ }
+ ;
+
+optArraySize:
+ {
+ $$ = 0;
+ }
+ | INTEGER
+ {
+ if (($1 < 1) || ((unsigned) $1 > state->limits->MaxParameters)) {
+ char msg[100];
+ _mesa_snprintf(msg, sizeof(msg),
+ "invalid parameter array size (size=%d max=%u)",
+ $1, state->limits->MaxParameters);
+ yyerror(& @1, state, msg);
+ YYERROR;
+ } else {
+ $$ = $1;
+ }
+ }
+ ;
+
+paramSingleInit: '=' paramSingleItemDecl
+ {
+ $$ = $2;
+ }
+ ;
+
+paramMultipleInit: '=' '{' paramMultInitList '}'
+ {
+ $$ = $3;
+ }
+ ;
+
+paramMultInitList: paramMultipleItem
+ | paramMultInitList ',' paramMultipleItem
+ {
+ $1.param_binding_length += $3.param_binding_length;
+ $$ = $1;
+ }
+ ;
+
+paramSingleItemDecl: stateSingleItem
+ {
+ memset(& $$, 0, sizeof($$));
+ $$.param_binding_begin = ~0;
+ initialize_symbol_from_state(state->prog, & $$, $1);
+ }
+ | programSingleItem
+ {
+ memset(& $$, 0, sizeof($$));
+ $$.param_binding_begin = ~0;
+ initialize_symbol_from_param(state->prog, & $$, $1);
+ }
+ | paramConstDecl
+ {
+ memset(& $$, 0, sizeof($$));
+ $$.param_binding_begin = ~0;
+ initialize_symbol_from_const(state->prog, & $$, & $1, GL_TRUE);
+ }
+ ;
+
+paramSingleItemUse: stateSingleItem
+ {
+ memset(& $$, 0, sizeof($$));
+ $$.param_binding_begin = ~0;
+ initialize_symbol_from_state(state->prog, & $$, $1);
+ }
+ | programSingleItem
+ {
+ memset(& $$, 0, sizeof($$));
+ $$.param_binding_begin = ~0;
+ initialize_symbol_from_param(state->prog, & $$, $1);
+ }
+ | paramConstUse
+ {
+ memset(& $$, 0, sizeof($$));
+ $$.param_binding_begin = ~0;
+ initialize_symbol_from_const(state->prog, & $$, & $1, GL_TRUE);
+ }
+ ;
+
+paramMultipleItem: stateMultipleItem
+ {
+ memset(& $$, 0, sizeof($$));
+ $$.param_binding_begin = ~0;
+ initialize_symbol_from_state(state->prog, & $$, $1);
+ }
+ | programMultipleItem
+ {
+ memset(& $$, 0, sizeof($$));
+ $$.param_binding_begin = ~0;
+ initialize_symbol_from_param(state->prog, & $$, $1);
+ }
+ | paramConstDecl
+ {
+ memset(& $$, 0, sizeof($$));
+ $$.param_binding_begin = ~0;
+ initialize_symbol_from_const(state->prog, & $$, & $1, GL_FALSE);
+ }
+ ;
+
+stateMultipleItem: stateSingleItem { memcpy($$, $1, sizeof($$)); }
+ | STATE stateMatrixRows { memcpy($$, $2, sizeof($$)); }
+ ;
+
+stateSingleItem: STATE stateMaterialItem { memcpy($$, $2, sizeof($$)); }
+ | STATE stateLightItem { memcpy($$, $2, sizeof($$)); }
+ | STATE stateLightModelItem { memcpy($$, $2, sizeof($$)); }
+ | STATE stateLightProdItem { memcpy($$, $2, sizeof($$)); }
+ | STATE stateTexGenItem { memcpy($$, $2, sizeof($$)); }
+ | STATE stateTexEnvItem { memcpy($$, $2, sizeof($$)); }
+ | STATE stateFogItem { memcpy($$, $2, sizeof($$)); }
+ | STATE stateClipPlaneItem { memcpy($$, $2, sizeof($$)); }
+ | STATE statePointItem { memcpy($$, $2, sizeof($$)); }
+ | STATE stateMatrixRow { memcpy($$, $2, sizeof($$)); }
+ | STATE stateDepthItem { memcpy($$, $2, sizeof($$)); }
+ ;
+
+stateMaterialItem: MATERIAL optFaceType stateMatProperty
+ {
+ memset($$, 0, sizeof($$));
+ $$[0] = STATE_MATERIAL;
+ $$[1] = $2;
+ $$[2] = $3;
+ }
+ ;
+
+stateMatProperty: ambDiffSpecProperty
+ {
+ $$ = $1;
+ }
+ | EMISSION
+ {
+ $$ = STATE_EMISSION;
+ }
+ | SHININESS
+ {
+ $$ = STATE_SHININESS;
+ }
+ ;
+
+stateLightItem: LIGHT '[' stateLightNumber ']' stateLightProperty
+ {
+ memset($$, 0, sizeof($$));
+ $$[0] = STATE_LIGHT;
+ $$[1] = $3;
+ $$[2] = $5;
+ }
+ ;
+
+stateLightProperty: ambDiffSpecProperty
+ {
+ $$ = $1;
+ }
+ | POSITION
+ {
+ $$ = STATE_POSITION;
+ }
+ | ATTENUATION
+ {
+ if (!state->ctx->Extensions.EXT_point_parameters) {
+ yyerror(& @1, state, "GL_ARB_point_parameters not supported");
+ YYERROR;
+ }
+
+ $$ = STATE_ATTENUATION;
+ }
+ | SPOT stateSpotProperty
+ {
+ $$ = $2;
+ }
+ | HALF
+ {
+ $$ = STATE_HALF_VECTOR;
+ }
+ ;
+
+stateSpotProperty: DIRECTION
+ {
+ $$ = STATE_SPOT_DIRECTION;
+ }
+ ;
+
+stateLightModelItem: LIGHTMODEL stateLModProperty
+ {
+ $$[0] = $2[0];
+ $$[1] = $2[1];
+ }
+ ;
+
+stateLModProperty: AMBIENT
+ {
+ memset($$, 0, sizeof($$));
+ $$[0] = STATE_LIGHTMODEL_AMBIENT;
+ }
+ | optFaceType SCENECOLOR
+ {
+ memset($$, 0, sizeof($$));
+ $$[0] = STATE_LIGHTMODEL_SCENECOLOR;
+ $$[1] = $1;
+ }
+ ;
+
+stateLightProdItem: LIGHTPROD '[' stateLightNumber ']' optFaceType stateLProdProperty
+ {
+ memset($$, 0, sizeof($$));
+ $$[0] = STATE_LIGHTPROD;
+ $$[1] = $3;
+ $$[2] = $5;
+ $$[3] = $6;
+ }
+ ;
+
+stateLProdProperty: ambDiffSpecProperty;
+
+stateTexEnvItem: TEXENV optLegacyTexUnitNum stateTexEnvProperty
+ {
+ memset($$, 0, sizeof($$));
+ $$[0] = $3;
+ $$[1] = $2;
+ }
+ ;
+
+stateTexEnvProperty: COLOR
+ {
+ $$ = STATE_TEXENV_COLOR;
+ }
+ ;
+
+ambDiffSpecProperty: AMBIENT
+ {
+ $$ = STATE_AMBIENT;
+ }
+ | DIFFUSE
+ {
+ $$ = STATE_DIFFUSE;
+ }
+ | SPECULAR
+ {
+ $$ = STATE_SPECULAR;
+ }
+ ;
+
+stateLightNumber: INTEGER
+ {
+ if ((unsigned) $1 >= state->MaxLights) {
+ yyerror(& @1, state, "invalid light selector");
+ YYERROR;
+ }
+
+ $$ = $1;
+ }
+ ;
+
+stateTexGenItem: TEXGEN optTexCoordUnitNum stateTexGenType stateTexGenCoord
+ {
+ memset($$, 0, sizeof($$));
+ $$[0] = STATE_TEXGEN;
+ $$[1] = $2;
+ $$[2] = $3 + $4;
+ }
+ ;
+
+stateTexGenType: EYE
+ {
+ $$ = STATE_TEXGEN_EYE_S;
+ }
+ | OBJECT
+ {
+ $$ = STATE_TEXGEN_OBJECT_S;
+ }
+ ;
+stateTexGenCoord: TEXGEN_S
+ {
+ $$ = STATE_TEXGEN_EYE_S - STATE_TEXGEN_EYE_S;
+ }
+ | TEXGEN_T
+ {
+ $$ = STATE_TEXGEN_EYE_T - STATE_TEXGEN_EYE_S;
+ }
+ | TEXGEN_R
+ {
+ $$ = STATE_TEXGEN_EYE_R - STATE_TEXGEN_EYE_S;
+ }
+ | TEXGEN_Q
+ {
+ $$ = STATE_TEXGEN_EYE_Q - STATE_TEXGEN_EYE_S;
+ }
+ ;
+
+stateFogItem: FOG stateFogProperty
+ {
+ memset($$, 0, sizeof($$));
+ $$[0] = $2;
+ }
+ ;
+
+stateFogProperty: COLOR
+ {
+ $$ = STATE_FOG_COLOR;
+ }
+ | PARAMS
+ {
+ $$ = STATE_FOG_PARAMS;
+ }
+ ;
+
+stateClipPlaneItem: CLIP '[' stateClipPlaneNum ']' PLANE
+ {
+ memset($$, 0, sizeof($$));
+ $$[0] = STATE_CLIPPLANE;
+ $$[1] = $3;
+ }
+ ;
+
+stateClipPlaneNum: INTEGER
+ {
+ if ((unsigned) $1 >= state->MaxClipPlanes) {
+ yyerror(& @1, state, "invalid clip plane selector");
+ YYERROR;
+ }
+
+ $$ = $1;
+ }
+ ;
+
+statePointItem: POINT_TOK statePointProperty
+ {
+ memset($$, 0, sizeof($$));
+ $$[0] = $2;
+ }
+ ;
+
+statePointProperty: SIZE_TOK
+ {
+ $$ = STATE_POINT_SIZE;
+ }
+ | ATTENUATION
+ {
+ $$ = STATE_POINT_ATTENUATION;
+ }
+ ;
+
+stateMatrixRow: stateMatrixItem ROW '[' stateMatrixRowNum ']'
+ {
+ $$[0] = $1[0];
+ $$[1] = $1[1];
+ $$[2] = $4;
+ $$[3] = $4;
+ $$[4] = $1[2];
+ }
+ ;
+
+stateMatrixRows: stateMatrixItem optMatrixRows
+ {
+ $$[0] = $1[0];
+ $$[1] = $1[1];
+ $$[2] = $2[2];
+ $$[3] = $2[3];
+ $$[4] = $1[2];
+ }
+ ;
+
+optMatrixRows:
+ {
+ $$[2] = 0;
+ $$[3] = 3;
+ }
+ | ROW '[' stateMatrixRowNum DOT_DOT stateMatrixRowNum ']'
+ {
+ /* It seems logical that the matrix row range specifier would have
+ * to specify a range or more than one row (i.e., $5 > $3).
+ * However, the ARB_vertex_program spec says "a program will fail
+ * to load if <a> is greater than <b>." This means that $3 == $5
+ * is valid.
+ */
+ if ($3 > $5) {
+ yyerror(& @3, state, "invalid matrix row range");
+ YYERROR;
+ }
+
+ $$[2] = $3;
+ $$[3] = $5;
+ }
+ ;
+
+stateMatrixItem: MATRIX stateMatrixName stateOptMatModifier
+ {
+ $$[0] = $2[0];
+ $$[1] = $2[1];
+ $$[2] = $3;
+ }
+ ;
+
+stateOptMatModifier:
+ {
+ $$ = 0;
+ }
+ | stateMatModifier
+ {
+ $$ = $1;
+ }
+ ;
+
+stateMatModifier: INVERSE
+ {
+ $$ = STATE_MATRIX_INVERSE;
+ }
+ | TRANSPOSE
+ {
+ $$ = STATE_MATRIX_TRANSPOSE;
+ }
+ | INVTRANS
+ {
+ $$ = STATE_MATRIX_INVTRANS;
+ }
+ ;
+
+stateMatrixRowNum: INTEGER
+ {
+ if ($1 > 3) {
+ yyerror(& @1, state, "invalid matrix row reference");
+ YYERROR;
+ }
+
+ $$ = $1;
+ }
+ ;
+
+stateMatrixName: MODELVIEW stateOptModMatNum
+ {
+ $$[0] = STATE_MODELVIEW_MATRIX;
+ $$[1] = $2;
+ }
+ | PROJECTION
+ {
+ $$[0] = STATE_PROJECTION_MATRIX;
+ $$[1] = 0;
+ }
+ | MVP
+ {
+ $$[0] = STATE_MVP_MATRIX;
+ $$[1] = 0;
+ }
+ | TEXTURE optTexCoordUnitNum
+ {
+ $$[0] = STATE_TEXTURE_MATRIX;
+ $$[1] = $2;
+ }
+ | PALETTE '[' statePaletteMatNum ']'
+ {
+ yyerror(& @1, state, "GL_ARB_matrix_palette not supported");
+ YYERROR;
+ }
+ | MAT_PROGRAM '[' stateProgramMatNum ']'
+ {
+ $$[0] = STATE_PROGRAM_MATRIX;
+ $$[1] = $3;
+ }
+ ;
+
+stateOptModMatNum:
+ {
+ $$ = 0;
+ }
+ | '[' stateModMatNum ']'
+ {
+ $$ = $2;
+ }
+ ;
+stateModMatNum: INTEGER
+ {
+ /* Since GL_ARB_vertex_blend isn't supported, only modelview matrix
+ * zero is valid.
+ */
+ if ($1 != 0) {
+ yyerror(& @1, state, "invalid modelview matrix index");
+ YYERROR;
+ }
+
+ $$ = $1;
+ }
+ ;
+statePaletteMatNum: INTEGER
+ {
+ /* Since GL_ARB_matrix_palette isn't supported, just let any value
+ * through here. The error will be generated later.
+ */
+ $$ = $1;
+ }
+ ;
+stateProgramMatNum: INTEGER
+ {
+ if ((unsigned) $1 >= state->MaxProgramMatrices) {
+ yyerror(& @1, state, "invalid program matrix selector");
+ YYERROR;
+ }
+
+ $$ = $1;
+ }
+ ;
+
+stateDepthItem: DEPTH RANGE
+ {
+ memset($$, 0, sizeof($$));
+ $$[0] = STATE_DEPTH_RANGE;
+ }
+ ;
+
+
+programSingleItem: progEnvParam | progLocalParam;
+
+programMultipleItem: progEnvParams | progLocalParams;
+
+progEnvParams: PROGRAM ENV '[' progEnvParamNums ']'
+ {
+ memset($$, 0, sizeof($$));
+ $$[0] = state->state_param_enum;
+ $$[1] = STATE_ENV;
+ $$[2] = $4[0];
+ $$[3] = $4[1];
+ }
+ ;
+
+progEnvParamNums: progEnvParamNum
+ {
+ $$[0] = $1;
+ $$[1] = $1;
+ }
+ | progEnvParamNum DOT_DOT progEnvParamNum
+ {
+ $$[0] = $1;
+ $$[1] = $3;
+ }
+ ;
+
+progEnvParam: PROGRAM ENV '[' progEnvParamNum ']'
+ {
+ memset($$, 0, sizeof($$));
+ $$[0] = state->state_param_enum;
+ $$[1] = STATE_ENV;
+ $$[2] = $4;
+ $$[3] = $4;
+ }
+ ;
+
+progLocalParams: PROGRAM LOCAL '[' progLocalParamNums ']'
+ {
+ memset($$, 0, sizeof($$));
+ $$[0] = state->state_param_enum;
+ $$[1] = STATE_LOCAL;
+ $$[2] = $4[0];
+ $$[3] = $4[1];
+ }
+
+progLocalParamNums: progLocalParamNum
+ {
+ $$[0] = $1;
+ $$[1] = $1;
+ }
+ | progLocalParamNum DOT_DOT progLocalParamNum
+ {
+ $$[0] = $1;
+ $$[1] = $3;
+ }
+ ;
+
+progLocalParam: PROGRAM LOCAL '[' progLocalParamNum ']'
+ {
+ memset($$, 0, sizeof($$));
+ $$[0] = state->state_param_enum;
+ $$[1] = STATE_LOCAL;
+ $$[2] = $4;
+ $$[3] = $4;
+ }
+ ;
+
+progEnvParamNum: INTEGER
+ {
+ if ((unsigned) $1 >= state->limits->MaxEnvParams) {
+ yyerror(& @1, state, "invalid environment parameter reference");
+ YYERROR;
+ }
+ $$ = $1;
+ }
+ ;
+
+progLocalParamNum: INTEGER
+ {
+ if ((unsigned) $1 >= state->limits->MaxLocalParams) {
+ yyerror(& @1, state, "invalid local parameter reference");
+ YYERROR;
+ }
+ $$ = $1;
+ }
+ ;
+
+
+
+paramConstDecl: paramConstScalarDecl | paramConstVector;
+paramConstUse: paramConstScalarUse | paramConstVector;
+
+paramConstScalarDecl: signedFloatConstant
+ {
+ $$.count = 4;
+ $$.data[0] = $1;
+ $$.data[1] = $1;
+ $$.data[2] = $1;
+ $$.data[3] = $1;
+ }
+ ;
+
+paramConstScalarUse: REAL
+ {
+ $$.count = 1;
+ $$.data[0] = $1;
+ $$.data[1] = $1;
+ $$.data[2] = $1;
+ $$.data[3] = $1;
+ }
+ | INTEGER
+ {
+ $$.count = 1;
+ $$.data[0] = (float) $1;
+ $$.data[1] = (float) $1;
+ $$.data[2] = (float) $1;
+ $$.data[3] = (float) $1;
+ }
+ ;
+
+paramConstVector: '{' signedFloatConstant '}'
+ {
+ $$.count = 4;
+ $$.data[0] = $2;
+ $$.data[1] = 0.0f;
+ $$.data[2] = 0.0f;
+ $$.data[3] = 1.0f;
+ }
+ | '{' signedFloatConstant ',' signedFloatConstant '}'
+ {
+ $$.count = 4;
+ $$.data[0] = $2;
+ $$.data[1] = $4;
+ $$.data[2] = 0.0f;
+ $$.data[3] = 1.0f;
+ }
+ | '{' signedFloatConstant ',' signedFloatConstant ','
+ signedFloatConstant '}'
+ {
+ $$.count = 4;
+ $$.data[0] = $2;
+ $$.data[1] = $4;
+ $$.data[2] = $6;
+ $$.data[3] = 1.0f;
+ }
+ | '{' signedFloatConstant ',' signedFloatConstant ','
+ signedFloatConstant ',' signedFloatConstant '}'
+ {
+ $$.count = 4;
+ $$.data[0] = $2;
+ $$.data[1] = $4;
+ $$.data[2] = $6;
+ $$.data[3] = $8;
+ }
+ ;
+
+signedFloatConstant: optionalSign REAL
+ {
+ $$ = ($1) ? -$2 : $2;
+ }
+ | optionalSign INTEGER
+ {
+ $$ = (float)(($1) ? -$2 : $2);
+ }
+ ;
+
+optionalSign: '+' { $$ = FALSE; }
+ | '-' { $$ = TRUE; }
+ | { $$ = FALSE; }
+ ;
+
+TEMP_statement: optVarSize TEMP { $<integer>$ = $2; } varNameList
+ ;
+
+optVarSize: string
+ {
+ /* NV_fragment_program_option defines the size qualifiers in a
+ * fairly broken way. "SHORT" or "LONG" can optionally be used
+ * before TEMP or OUTPUT. However, neither is a reserved word!
+ * This means that we have to parse it as an identifier, then check
+ * to make sure it's one of the valid values. *sigh*
+ *
+ * In addition, the grammar in the extension spec does *not* allow
+ * the size specifier to be optional, but all known implementations
+ * do.
+ */
+ if (!state->option.NV_fragment) {
+ yyerror(& @1, state, "unexpected IDENTIFIER");
+ YYERROR;
+ }
+
+ if (strcmp("SHORT", $1) == 0) {
+ } else if (strcmp("LONG", $1) == 0) {
+ } else {
+ char *const err_str =
+ make_error_string("invalid storage size specifier \"%s\"",
+ $1);
+
+ yyerror(& @1, state, (err_str != NULL)
+ ? err_str : "invalid storage size specifier");
+
+ if (err_str != NULL) {
+ free(err_str);
+ }
+
+ YYERROR;
+ }
+ }
+ |
+ {
+ }
+ ;
+
+ADDRESS_statement: ADDRESS { $<integer>$ = $1; } varNameList
+ ;
+
+varNameList: varNameList ',' IDENTIFIER
+ {
+ if (!declare_variable(state, $3, $<integer>0, & @3)) {
+ free($3);
+ YYERROR;
+ }
+ }
+ | IDENTIFIER
+ {
+ if (!declare_variable(state, $1, $<integer>0, & @1)) {
+ free($1);
+ YYERROR;
+ }
+ }
+ ;
+
+OUTPUT_statement: optVarSize OUTPUT IDENTIFIER '=' resultBinding
+ {
+ struct asm_symbol *const s =
+ declare_variable(state, $3, at_output, & @3);
+
+ if (s == NULL) {
+ free($3);
+ YYERROR;
+ } else {
+ s->output_binding = $5;
+ }
+ }
+ ;
+
+resultBinding: RESULT POSITION
+ {
+ if (state->mode == ARB_vertex) {
+ $$ = VERT_RESULT_HPOS;
+ } else {
+ yyerror(& @2, state, "invalid program result name");
+ YYERROR;
+ }
+ }
+ | RESULT FOGCOORD
+ {
+ if (state->mode == ARB_vertex) {
+ $$ = VERT_RESULT_FOGC;
+ } else {
+ yyerror(& @2, state, "invalid program result name");
+ YYERROR;
+ }
+ }
+ | RESULT resultColBinding
+ {
+ $$ = $2;
+ }
+ | RESULT POINTSIZE
+ {
+ if (state->mode == ARB_vertex) {
+ $$ = VERT_RESULT_PSIZ;
+ } else {
+ yyerror(& @2, state, "invalid program result name");
+ YYERROR;
+ }
+ }
+ | RESULT TEXCOORD optTexCoordUnitNum
+ {
+ if (state->mode == ARB_vertex) {
+ $$ = VERT_RESULT_TEX0 + $3;
+ } else {
+ yyerror(& @2, state, "invalid program result name");
+ YYERROR;
+ }
+ }
+ | RESULT DEPTH
+ {
+ if (state->mode == ARB_fragment) {
+ $$ = FRAG_RESULT_DEPTH;
+ } else {
+ yyerror(& @2, state, "invalid program result name");
+ YYERROR;
+ }
+ }
+ ;
+
+resultColBinding: COLOR optResultFaceType optResultColorType
+ {
+ $$ = $2 + $3;
+ }
+ ;
+
+optResultFaceType:
+ {
+ if (state->mode == ARB_vertex) {
+ $$ = VERT_RESULT_COL0;
+ } else {
+ if (state->option.DrawBuffers)
+ $$ = FRAG_RESULT_DATA0;
+ else
+ $$ = FRAG_RESULT_COLOR;
+ }
+ }
+ | '[' INTEGER ']'
+ {
+ if (state->mode == ARB_vertex) {
+ yyerror(& @1, state, "invalid program result name");
+ YYERROR;
+ } else {
+ if (!state->option.DrawBuffers) {
+ /* From the ARB_draw_buffers spec (same text exists
+ * for ATI_draw_buffers):
+ *
+ * If this option is not specified, a fragment
+ * program that attempts to bind
+ * "result.color[n]" will fail to load, and only
+ * "result.color" will be allowed.
+ */
+ yyerror(& @1, state,
+ "result.color[] used without "
+ "`OPTION ARB_draw_buffers' or "
+ "`OPTION ATI_draw_buffers'");
+ YYERROR;
+ } else if ($2 >= state->MaxDrawBuffers) {
+ yyerror(& @1, state,
+ "result.color[] exceeds MAX_DRAW_BUFFERS_ARB");
+ YYERROR;
+ }
+ $$ = FRAG_RESULT_DATA0 + $2;
+ }
+ }
+ | FRONT
+ {
+ if (state->mode == ARB_vertex) {
+ $$ = VERT_RESULT_COL0;
+ } else {
+ yyerror(& @1, state, "invalid program result name");
+ YYERROR;
+ }
+ }
+ | BACK
+ {
+ if (state->mode == ARB_vertex) {
+ $$ = VERT_RESULT_BFC0;
+ } else {
+ yyerror(& @1, state, "invalid program result name");
+ YYERROR;
+ }
+ }
+ ;
+
+optResultColorType:
+ {
+ $$ = 0;
+ }
+ | PRIMARY
+ {
+ if (state->mode == ARB_vertex) {
+ $$ = 0;
+ } else {
+ yyerror(& @1, state, "invalid program result name");
+ YYERROR;
+ }
+ }
+ | SECONDARY
+ {
+ if (state->mode == ARB_vertex) {
+ $$ = 1;
+ } else {
+ yyerror(& @1, state, "invalid program result name");
+ YYERROR;
+ }
+ }
+ ;
+
+optFaceType: { $$ = 0; }
+ | FRONT { $$ = 0; }
+ | BACK { $$ = 1; }
+ ;
+
+optColorType: { $$ = 0; }
+ | PRIMARY { $$ = 0; }
+ | SECONDARY { $$ = 1; }
+ ;
+
+optTexCoordUnitNum: { $$ = 0; }
+ | '[' texCoordUnitNum ']' { $$ = $2; }
+ ;
+
+optTexImageUnitNum: { $$ = 0; }
+ | '[' texImageUnitNum ']' { $$ = $2; }
+ ;
+
+optLegacyTexUnitNum: { $$ = 0; }
+ | '[' legacyTexUnitNum ']' { $$ = $2; }
+ ;
+
+texCoordUnitNum: INTEGER
+ {
+ if ((unsigned) $1 >= state->MaxTextureCoordUnits) {
+ yyerror(& @1, state, "invalid texture coordinate unit selector");
+ YYERROR;
+ }
+
+ $$ = $1;
+ }
+ ;
+
+texImageUnitNum: INTEGER
+ {
+ if ((unsigned) $1 >= state->MaxTextureImageUnits) {
+ yyerror(& @1, state, "invalid texture image unit selector");
+ YYERROR;
+ }
+
+ $$ = $1;
+ }
+ ;
+
+legacyTexUnitNum: INTEGER
+ {
+ if ((unsigned) $1 >= state->MaxTextureUnits) {
+ yyerror(& @1, state, "invalid texture unit selector");
+ YYERROR;
+ }
+
+ $$ = $1;
+ }
+ ;
+
+ALIAS_statement: ALIAS IDENTIFIER '=' USED_IDENTIFIER
+ {
+ struct asm_symbol *exist = (struct asm_symbol *)
+ _mesa_symbol_table_find_symbol(state->st, 0, $2);
+ struct asm_symbol *target = (struct asm_symbol *)
+ _mesa_symbol_table_find_symbol(state->st, 0, $4);
+
+ free($4);
+
+ if (exist != NULL) {
+ char m[1000];
+ _mesa_snprintf(m, sizeof(m), "redeclared identifier: %s", $2);
+ free($2);
+ yyerror(& @2, state, m);
+ YYERROR;
+ } else if (target == NULL) {
+ free($2);
+ yyerror(& @4, state,
+ "undefined variable binding in ALIAS statement");
+ YYERROR;
+ } else {
+ _mesa_symbol_table_add_symbol(state->st, 0, $2, target);
+ }
+ }
+ ;
+
+string: IDENTIFIER
+ | USED_IDENTIFIER
+ ;
+
+%%
+
+void
+asm_instruction_set_operands(struct asm_instruction *inst,
+ const struct prog_dst_register *dst,
+ const struct asm_src_register *src0,
+ const struct asm_src_register *src1,
+ const struct asm_src_register *src2)
+{
+ /* In the core ARB extensions only the KIL instruction doesn't have a
+ * destination register.
+ */
+ if (dst == NULL) {
+ init_dst_reg(& inst->Base.DstReg);
+ } else {
+ inst->Base.DstReg = *dst;
+ }
+
+ /* The only instruction that doesn't have any source registers is the
+ * condition-code based KIL instruction added by NV_fragment_program_option.
+ */
+ if (src0 != NULL) {
+ inst->Base.SrcReg[0] = src0->Base;
+ inst->SrcReg[0] = *src0;
+ } else {
+ init_src_reg(& inst->SrcReg[0]);
+ }
+
+ if (src1 != NULL) {
+ inst->Base.SrcReg[1] = src1->Base;
+ inst->SrcReg[1] = *src1;
+ } else {
+ init_src_reg(& inst->SrcReg[1]);
+ }
+
+ if (src2 != NULL) {
+ inst->Base.SrcReg[2] = src2->Base;
+ inst->SrcReg[2] = *src2;
+ } else {
+ init_src_reg(& inst->SrcReg[2]);
+ }
+}
+
+
+struct asm_instruction *
+asm_instruction_ctor(gl_inst_opcode op,
+ const struct prog_dst_register *dst,
+ const struct asm_src_register *src0,
+ const struct asm_src_register *src1,
+ const struct asm_src_register *src2)
+{
+ struct asm_instruction *inst = CALLOC_STRUCT(asm_instruction);
+
+ if (inst) {
+ _mesa_init_instructions(& inst->Base, 1);
+ inst->Base.Opcode = op;
+
+ asm_instruction_set_operands(inst, dst, src0, src1, src2);
+ }
+
+ return inst;
+}
+
+
+struct asm_instruction *
+asm_instruction_copy_ctor(const struct prog_instruction *base,
+ const struct prog_dst_register *dst,
+ const struct asm_src_register *src0,
+ const struct asm_src_register *src1,
+ const struct asm_src_register *src2)
+{
+ struct asm_instruction *inst = CALLOC_STRUCT(asm_instruction);
+
+ if (inst) {
+ _mesa_init_instructions(& inst->Base, 1);
+ inst->Base.Opcode = base->Opcode;
+ inst->Base.CondUpdate = base->CondUpdate;
+ inst->Base.CondDst = base->CondDst;
+ inst->Base.SaturateMode = base->SaturateMode;
+ inst->Base.Precision = base->Precision;
+
+ asm_instruction_set_operands(inst, dst, src0, src1, src2);
+ }
+
+ return inst;
+}
+
+
+void
+init_dst_reg(struct prog_dst_register *r)
+{
+ memset(r, 0, sizeof(*r));
+ r->File = PROGRAM_UNDEFINED;
+ r->WriteMask = WRITEMASK_XYZW;
+ r->CondMask = COND_TR;
+ r->CondSwizzle = SWIZZLE_NOOP;
+}
+
+
+/** Like init_dst_reg() but set the File and Index fields. */
+void
+set_dst_reg(struct prog_dst_register *r, gl_register_file file, GLint index)
+{
+ const GLint maxIndex = 1 << INST_INDEX_BITS;
+ const GLint minIndex = 0;
+ ASSERT(index >= minIndex);
+ (void) minIndex;
+ ASSERT(index <= maxIndex);
+ (void) maxIndex;
+ ASSERT(file == PROGRAM_TEMPORARY ||
+ file == PROGRAM_ADDRESS ||
+ file == PROGRAM_OUTPUT);
+ memset(r, 0, sizeof(*r));
+ r->File = file;
+ r->Index = index;
+ r->WriteMask = WRITEMASK_XYZW;
+ r->CondMask = COND_TR;
+ r->CondSwizzle = SWIZZLE_NOOP;
+}
+
+
+void
+init_src_reg(struct asm_src_register *r)
+{
+ memset(r, 0, sizeof(*r));
+ r->Base.File = PROGRAM_UNDEFINED;
+ r->Base.Swizzle = SWIZZLE_NOOP;
+ r->Symbol = NULL;
+}
+
+
+/** Like init_src_reg() but set the File and Index fields.
+ * \return GL_TRUE if a valid src register, GL_FALSE otherwise
+ */
+void
+set_src_reg(struct asm_src_register *r, gl_register_file file, GLint index)
+{
+ set_src_reg_swz(r, file, index, SWIZZLE_XYZW);
+}
+
+
+void
+set_src_reg_swz(struct asm_src_register *r, gl_register_file file, GLint index,
+ GLuint swizzle)
+{
+ const GLint maxIndex = (1 << INST_INDEX_BITS) - 1;
+ const GLint minIndex = -(1 << INST_INDEX_BITS);
+ ASSERT(file < PROGRAM_FILE_MAX);
+ ASSERT(index >= minIndex);
+ (void) minIndex;
+ ASSERT(index <= maxIndex);
+ (void) maxIndex;
+ memset(r, 0, sizeof(*r));
+ r->Base.File = file;
+ r->Base.Index = index;
+ r->Base.Swizzle = swizzle;
+ r->Symbol = NULL;
+}
+
+
+/**
+ * Validate the set of inputs used by a program
+ *
+ * Validates that legal sets of inputs are used by the program. In this case
+ * "used" included both reading the input or binding the input to a name using
+ * the \c ATTRIB command.
+ *
+ * \return
+ * \c TRUE if the combination of inputs used is valid, \c FALSE otherwise.
+ */
+int
+validate_inputs(struct YYLTYPE *locp, struct asm_parser_state *state)
+{
+ const int inputs = state->prog->InputsRead | state->InputsBound;
+
+ if (((inputs & 0x0ffff) & (inputs >> 16)) != 0) {
+ yyerror(locp, state, "illegal use of generic attribute and name attribute");
+ return 0;
+ }
+
+ return 1;
+}
+
+
+struct asm_symbol *
+declare_variable(struct asm_parser_state *state, char *name, enum asm_type t,
+ struct YYLTYPE *locp)
+{
+ struct asm_symbol *s = NULL;
+ struct asm_symbol *exist = (struct asm_symbol *)
+ _mesa_symbol_table_find_symbol(state->st, 0, name);
+
+
+ if (exist != NULL) {
+ yyerror(locp, state, "redeclared identifier");
+ } else {
+ s = calloc(1, sizeof(struct asm_symbol));
+ s->name = name;
+ s->type = t;
+
+ switch (t) {
+ case at_temp:
+ if (state->prog->NumTemporaries >= state->limits->MaxTemps) {
+ yyerror(locp, state, "too many temporaries declared");
+ free(s);
+ return NULL;
+ }
+
+ s->temp_binding = state->prog->NumTemporaries;
+ state->prog->NumTemporaries++;
+ break;
+
+ case at_address:
+ if (state->prog->NumAddressRegs >= state->limits->MaxAddressRegs) {
+ yyerror(locp, state, "too many address registers declared");
+ free(s);
+ return NULL;
+ }
+
+ /* FINISHME: Add support for multiple address registers.
+ */
+ state->prog->NumAddressRegs++;
+ break;
+
+ default:
+ break;
+ }
+
+ _mesa_symbol_table_add_symbol(state->st, 0, s->name, s);
+ s->next = state->sym;
+ state->sym = s;
+ }
+
+ return s;
+}
+
+
+int add_state_reference(struct gl_program_parameter_list *param_list,
+ const gl_state_index tokens[STATE_LENGTH])
+{
+ const GLuint size = 4; /* XXX fix */
+ char *name;
+ GLint index;
+
+ name = _mesa_program_state_string(tokens);
+ index = _mesa_add_parameter(param_list, PROGRAM_STATE_VAR, name,
+ size, GL_NONE, NULL, tokens, 0x0);
+ param_list->StateFlags |= _mesa_program_state_flags(tokens);
+
+ /* free name string here since we duplicated it in add_parameter() */
+ free(name);
+
+ return index;
+}
+
+
+int
+initialize_symbol_from_state(struct gl_program *prog,
+ struct asm_symbol *param_var,
+ const gl_state_index tokens[STATE_LENGTH])
+{
+ int idx = -1;
+ gl_state_index state_tokens[STATE_LENGTH];
+
+
+ memcpy(state_tokens, tokens, sizeof(state_tokens));
+
+ param_var->type = at_param;
+ param_var->param_binding_type = PROGRAM_STATE_VAR;
+
+ /* If we are adding a STATE_MATRIX that has multiple rows, we need to
+ * unroll it and call add_state_reference() for each row
+ */
+ if ((state_tokens[0] == STATE_MODELVIEW_MATRIX ||
+ state_tokens[0] == STATE_PROJECTION_MATRIX ||
+ state_tokens[0] == STATE_MVP_MATRIX ||
+ state_tokens[0] == STATE_TEXTURE_MATRIX ||
+ state_tokens[0] == STATE_PROGRAM_MATRIX)
+ && (state_tokens[2] != state_tokens[3])) {
+ int row;
+ const int first_row = state_tokens[2];
+ const int last_row = state_tokens[3];
+
+ for (row = first_row; row <= last_row; row++) {
+ state_tokens[2] = state_tokens[3] = row;
+
+ idx = add_state_reference(prog->Parameters, state_tokens);
+ if (param_var->param_binding_begin == ~0U) {
+ param_var->param_binding_begin = idx;
+ param_var->param_binding_swizzle = SWIZZLE_XYZW;
+ }
+
+ param_var->param_binding_length++;
+ }
+ }
+ else {
+ idx = add_state_reference(prog->Parameters, state_tokens);
+ if (param_var->param_binding_begin == ~0U) {
+ param_var->param_binding_begin = idx;
+ param_var->param_binding_swizzle = SWIZZLE_XYZW;
+ }
+ param_var->param_binding_length++;
+ }
+
+ return idx;
+}
+
+
+int
+initialize_symbol_from_param(struct gl_program *prog,
+ struct asm_symbol *param_var,
+ const gl_state_index tokens[STATE_LENGTH])
+{
+ int idx = -1;
+ gl_state_index state_tokens[STATE_LENGTH];
+
+
+ memcpy(state_tokens, tokens, sizeof(state_tokens));
+
+ assert((state_tokens[0] == STATE_VERTEX_PROGRAM)
+ || (state_tokens[0] == STATE_FRAGMENT_PROGRAM));
+ assert((state_tokens[1] == STATE_ENV)
+ || (state_tokens[1] == STATE_LOCAL));
+
+ /*
+ * The param type is STATE_VAR. The program parameter entry will
+ * effectively be a pointer into the LOCAL or ENV parameter array.
+ */
+ param_var->type = at_param;
+ param_var->param_binding_type = PROGRAM_STATE_VAR;
+
+ /* If we are adding a STATE_ENV or STATE_LOCAL that has multiple elements,
+ * we need to unroll it and call add_state_reference() for each row
+ */
+ if (state_tokens[2] != state_tokens[3]) {
+ int row;
+ const int first_row = state_tokens[2];
+ const int last_row = state_tokens[3];
+
+ for (row = first_row; row <= last_row; row++) {
+ state_tokens[2] = state_tokens[3] = row;
+
+ idx = add_state_reference(prog->Parameters, state_tokens);
+ if (param_var->param_binding_begin == ~0U) {
+ param_var->param_binding_begin = idx;
+ param_var->param_binding_swizzle = SWIZZLE_XYZW;
+ }
+ param_var->param_binding_length++;
+ }
+ }
+ else {
+ idx = add_state_reference(prog->Parameters, state_tokens);
+ if (param_var->param_binding_begin == ~0U) {
+ param_var->param_binding_begin = idx;
+ param_var->param_binding_swizzle = SWIZZLE_XYZW;
+ }
+ param_var->param_binding_length++;
+ }
+
+ return idx;
+}
+
+
+/**
+ * Put a float/vector constant/literal into the parameter list.
+ * \param param_var returns info about the parameter/constant's location,
+ * binding, type, etc.
+ * \param vec the vector/constant to add
+ * \param allowSwizzle if true, try to consolidate constants which only differ
+ * by a swizzle. We don't want to do this when building
+ * arrays of constants that may be indexed indirectly.
+ * \return index of the constant in the parameter list.
+ */
+int
+initialize_symbol_from_const(struct gl_program *prog,
+ struct asm_symbol *param_var,
+ const struct asm_vector *vec,
+ GLboolean allowSwizzle)
+{
+ unsigned swizzle;
+ const int idx = _mesa_add_unnamed_constant(prog->Parameters,
+ vec->data, vec->count,
+ allowSwizzle ? &swizzle : NULL);
+
+ param_var->type = at_param;
+ param_var->param_binding_type = PROGRAM_CONSTANT;
+
+ if (param_var->param_binding_begin == ~0U) {
+ param_var->param_binding_begin = idx;
+ param_var->param_binding_swizzle = allowSwizzle ? swizzle : SWIZZLE_XYZW;
+ }
+ param_var->param_binding_length++;
+
+ return idx;
+}
+
+
+char *
+make_error_string(const char *fmt, ...)
+{
+ int length;
+ char *str;
+ va_list args;
+
+
+ /* Call vsnprintf once to determine how large the final string is. Call it
+ * again to do the actual formatting. from the vsnprintf manual page:
+ *
+ * Upon successful return, these functions return the number of
+ * characters printed (not including the trailing '\0' used to end
+ * output to strings).
+ */
+ va_start(args, fmt);
+ length = 1 + vsnprintf(NULL, 0, fmt, args);
+ va_end(args);
+
+ str = malloc(length);
+ if (str) {
+ va_start(args, fmt);
+ vsnprintf(str, length, fmt, args);
+ va_end(args);
+ }
+
+ return str;
+}
+
+
+void
+yyerror(YYLTYPE *locp, struct asm_parser_state *state, const char *s)
+{
+ char *err_str;
+
+
+ err_str = make_error_string("glProgramStringARB(%s)\n", s);
+ if (err_str) {
+ _mesa_error(state->ctx, GL_INVALID_OPERATION, "%s", err_str);
+ free(err_str);
+ }
+
+ err_str = make_error_string("line %u, char %u: error: %s\n",
+ locp->first_line, locp->first_column, s);
+ _mesa_set_program_error(state->ctx, locp->position, err_str);
+
+ if (err_str) {
+ free(err_str);
+ }
+}
+
+
+GLboolean
+_mesa_parse_arb_program(struct gl_context *ctx, GLenum target, const GLubyte *str,
+ GLsizei len, struct asm_parser_state *state)
+{
+ struct asm_instruction *inst;
+ unsigned i;
+ GLubyte *strz;
+ GLboolean result = GL_FALSE;
+ void *temp;
+ struct asm_symbol *sym;
+
+ state->ctx = ctx;
+ state->prog->Target = target;
+ state->prog->Parameters = _mesa_new_parameter_list();
+
+ /* Make a copy of the program string and force it to be NUL-terminated.
+ */
+ strz = (GLubyte *) malloc(len + 1);
+ if (strz == NULL) {
+ _mesa_error(ctx, GL_OUT_OF_MEMORY, "glProgramStringARB");
+ return GL_FALSE;
+ }
+ memcpy (strz, str, len);
+ strz[len] = '\0';
+
+ state->prog->String = strz;
+
+ state->st = _mesa_symbol_table_ctor();
+
+ state->limits = (target == GL_VERTEX_PROGRAM_ARB)
+ ? & ctx->Const.VertexProgram
+ : & ctx->Const.FragmentProgram;
+
+ state->MaxTextureImageUnits = ctx->Const.MaxTextureImageUnits;
+ state->MaxTextureCoordUnits = ctx->Const.MaxTextureCoordUnits;
+ state->MaxTextureUnits = ctx->Const.MaxTextureUnits;
+ state->MaxClipPlanes = ctx->Const.MaxClipPlanes;
+ state->MaxLights = ctx->Const.MaxLights;
+ state->MaxProgramMatrices = ctx->Const.MaxProgramMatrices;
+ state->MaxDrawBuffers = ctx->Const.MaxDrawBuffers;
+
+ state->state_param_enum = (target == GL_VERTEX_PROGRAM_ARB)
+ ? STATE_VERTEX_PROGRAM : STATE_FRAGMENT_PROGRAM;
+
+ _mesa_set_program_error(ctx, -1, NULL);
+
+ _mesa_program_lexer_ctor(& state->scanner, state, (const char *) str, len);
+ yyparse(state);
+ _mesa_program_lexer_dtor(state->scanner);
+
+
+ if (ctx->Program.ErrorPos != -1) {
+ goto error;
+ }
+
+ if (! _mesa_layout_parameters(state)) {
+ struct YYLTYPE loc;
+
+ loc.first_line = 0;
+ loc.first_column = 0;
+ loc.position = len;
+
+ yyerror(& loc, state, "invalid PARAM usage");
+ goto error;
+ }
+
+
+
+ /* Add one instruction to store the "END" instruction.
+ */
+ state->prog->Instructions =
+ _mesa_alloc_instructions(state->prog->NumInstructions + 1);
+ inst = state->inst_head;
+ for (i = 0; i < state->prog->NumInstructions; i++) {
+ struct asm_instruction *const temp = inst->next;
+
+ state->prog->Instructions[i] = inst->Base;
+ inst = temp;
+ }
+
+ /* Finally, tag on an OPCODE_END instruction */
+ {
+ const GLuint numInst = state->prog->NumInstructions;
+ _mesa_init_instructions(state->prog->Instructions + numInst, 1);
+ state->prog->Instructions[numInst].Opcode = OPCODE_END;
+ }
+ state->prog->NumInstructions++;
+
+ state->prog->NumParameters = state->prog->Parameters->NumParameters;
+ state->prog->NumAttributes = _mesa_bitcount(state->prog->InputsRead);
+
+ /*
+ * Initialize native counts to logical counts. The device driver may
+ * change them if program is translated into a hardware program.
+ */
+ state->prog->NumNativeInstructions = state->prog->NumInstructions;
+ state->prog->NumNativeTemporaries = state->prog->NumTemporaries;
+ state->prog->NumNativeParameters = state->prog->NumParameters;
+ state->prog->NumNativeAttributes = state->prog->NumAttributes;
+ state->prog->NumNativeAddressRegs = state->prog->NumAddressRegs;
+
+ result = GL_TRUE;
+
+error:
+ for (inst = state->inst_head; inst != NULL; inst = temp) {
+ temp = inst->next;
+ free(inst);
+ }
+
+ state->inst_head = NULL;
+ state->inst_tail = NULL;
+
+ for (sym = state->sym; sym != NULL; sym = temp) {
+ temp = sym->next;
+
+ free((void *) sym->name);
+ free(sym);
+ }
+ state->sym = NULL;
+
+ _mesa_symbol_table_dtor(state->st);
+ state->st = NULL;
+
+ return result;
+}
diff --git a/mesalib/src/mesa/program/program_parse_extra.c b/mesalib/src/mesa/program/program_parse_extra.c index 4d928483e..67ab9b99d 100644 --- a/mesalib/src/mesa/program/program_parse_extra.c +++ b/mesalib/src/mesa/program/program_parse_extra.c @@ -1,265 +1,265 @@ -/* - * Copyright © 2009 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. - */ - -#include <string.h> -#include "main/mtypes.h" -#include "prog_instruction.h" -#include "program_parser.h" - - -/** - * Extra assembly-level parser routines - * - * \author Ian Romanick <ian.d.romanick@intel.com> - */ - -int -_mesa_parse_instruction_suffix(const struct asm_parser_state *state, - const char *suffix, - struct prog_instruction *inst) -{ - inst->CondUpdate = 0; - inst->CondDst = 0; - inst->SaturateMode = SATURATE_OFF; - inst->Precision = FLOAT32; - - - /* The first possible suffix element is the precision specifier from - * NV_fragment_program_option. - */ - if (state->option.NV_fragment) { - switch (suffix[0]) { - case 'H': - inst->Precision = FLOAT16; - suffix++; - break; - case 'R': - inst->Precision = FLOAT32; - suffix++; - break; - case 'X': - inst->Precision = FIXED12; - suffix++; - break; - default: - break; - } - } - - /* The next possible suffix element is the condition code modifier selection - * from NV_fragment_program_option. - */ - if (state->option.NV_fragment) { - if (suffix[0] == 'C') { - inst->CondUpdate = 1; - suffix++; - } - } - - - /* The final possible suffix element is the saturation selector from - * ARB_fragment_program. - */ - if (state->mode == ARB_fragment) { - if (strcmp(suffix, "_SAT") == 0) { - inst->SaturateMode = SATURATE_ZERO_ONE; - suffix += 4; - } - } - - - /* It is an error for all of the suffix string not to be consumed. - */ - return suffix[0] == '\0'; -} - - -int -_mesa_parse_cc(const char *s) -{ - int cond = 0; - - switch (s[0]) { - case 'E': - if (s[1] == 'Q') { - cond = COND_EQ; - } - break; - - case 'F': - if (s[1] == 'L') { - cond = COND_FL; - } - break; - - case 'G': - if (s[1] == 'E') { - cond = COND_GE; - } else if (s[1] == 'T') { - cond = COND_GT; - } - break; - - case 'L': - if (s[1] == 'E') { - cond = COND_LE; - } else if (s[1] == 'T') { - cond = COND_LT; - } - break; - - case 'N': - if (s[1] == 'E') { - cond = COND_NE; - } - break; - - case 'T': - if (s[1] == 'R') { - cond = COND_TR; - } - break; - - default: - break; - } - - return ((cond == 0) || (s[2] != '\0')) ? 0 : cond; -} - - -int -_mesa_ARBvp_parse_option(struct asm_parser_state *state, const char *option) -{ - if (strcmp(option, "ARB_position_invariant") == 0) { - state->option.PositionInvariant = 1; - return 1; - } - - return 0; -} - - -int -_mesa_ARBfp_parse_option(struct asm_parser_state *state, const char *option) -{ - /* All of the options currently supported start with "ARB_". The code is - * currently structured with nested if-statements because eventually options - * that start with "NV_" will be supported. This structure will result in - * less churn when those options are added. - */ - if (strncmp(option, "ARB_", 4) == 0) { - /* Advance the pointer past the "ARB_" prefix. - */ - option += 4; - - - if (strncmp(option, "fog_", 4) == 0) { - option += 4; - - if (state->option.Fog == OPTION_NONE) { - if (strcmp(option, "exp") == 0) { - state->option.Fog = OPTION_FOG_EXP; - return 1; - } else if (strcmp(option, "exp2") == 0) { - state->option.Fog = OPTION_FOG_EXP2; - return 1; - } else if (strcmp(option, "linear") == 0) { - state->option.Fog = OPTION_FOG_LINEAR; - return 1; - } - } - - return 0; - } else if (strncmp(option, "precision_hint_", 15) == 0) { - option += 15; - - if (state->option.PrecisionHint == OPTION_NONE) { - if (strcmp(option, "nicest") == 0) { - state->option.PrecisionHint = OPTION_NICEST; - return 1; - } else if (strcmp(option, "fastest") == 0) { - state->option.PrecisionHint = OPTION_FASTEST; - return 1; - } - } - - return 0; - } else if (strcmp(option, "draw_buffers") == 0) { - /* Don't need to check extension availability because all Mesa-based - * drivers support GL_ARB_draw_buffers. - */ - state->option.DrawBuffers = 1; - return 1; - } else if (strcmp(option, "fragment_program_shadow") == 0) { - if (state->ctx->Extensions.ARB_fragment_program_shadow) { - state->option.Shadow = 1; - return 1; - } - } else if (strncmp(option, "fragment_coord_", 15) == 0) { - option += 15; - if (state->ctx->Extensions.ARB_fragment_coord_conventions) { - if (strcmp(option, "origin_upper_left") == 0) { - state->option.OriginUpperLeft = 1; - return 1; - } - else if (strcmp(option, "pixel_center_integer") == 0) { - state->option.PixelCenterInteger = 1; - return 1; - } - } - } - } else if (strncmp(option, "ATI_", 4) == 0) { - option += 4; - - if (strcmp(option, "draw_buffers") == 0) { - /* Don't need to check extension availability because all Mesa-based - * drivers support GL_ATI_draw_buffers. - */ - state->option.DrawBuffers = 1; - return 1; - } - } else if (strncmp(option, "NV_fragment_program", 19) == 0) { - option += 19; - - /* Other NV_fragment_program strings may be supported later. - */ - if (option[0] == '\0') { - if (state->ctx->Extensions.NV_fragment_program_option) { - state->option.NV_fragment = 1; - return 1; - } - } - } else if (strncmp(option, "MESA_", 5) == 0) { - option += 5; - - if (strcmp(option, "texture_array") == 0) { - if (state->ctx->Extensions.MESA_texture_array) { - state->option.TexArray = 1; - return 1; - } - } - } - - return 0; -} +/*
+ * Copyright © 2009 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.
+ */
+
+#include <string.h>
+#include "main/mtypes.h"
+#include "prog_instruction.h"
+#include "program_parser.h"
+
+
+/**
+ * Extra assembly-level parser routines
+ *
+ * \author Ian Romanick <ian.d.romanick@intel.com>
+ */
+
+int
+_mesa_parse_instruction_suffix(const struct asm_parser_state *state,
+ const char *suffix,
+ struct prog_instruction *inst)
+{
+ inst->CondUpdate = 0;
+ inst->CondDst = 0;
+ inst->SaturateMode = SATURATE_OFF;
+ inst->Precision = FLOAT32;
+
+
+ /* The first possible suffix element is the precision specifier from
+ * NV_fragment_program_option.
+ */
+ if (state->option.NV_fragment) {
+ switch (suffix[0]) {
+ case 'H':
+ inst->Precision = FLOAT16;
+ suffix++;
+ break;
+ case 'R':
+ inst->Precision = FLOAT32;
+ suffix++;
+ break;
+ case 'X':
+ inst->Precision = FIXED12;
+ suffix++;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* The next possible suffix element is the condition code modifier selection
+ * from NV_fragment_program_option.
+ */
+ if (state->option.NV_fragment) {
+ if (suffix[0] == 'C') {
+ inst->CondUpdate = 1;
+ suffix++;
+ }
+ }
+
+
+ /* The final possible suffix element is the saturation selector from
+ * ARB_fragment_program.
+ */
+ if (state->mode == ARB_fragment) {
+ if (strcmp(suffix, "_SAT") == 0) {
+ inst->SaturateMode = SATURATE_ZERO_ONE;
+ suffix += 4;
+ }
+ }
+
+
+ /* It is an error for all of the suffix string not to be consumed.
+ */
+ return suffix[0] == '\0';
+}
+
+
+int
+_mesa_parse_cc(const char *s)
+{
+ int cond = 0;
+
+ switch (s[0]) {
+ case 'E':
+ if (s[1] == 'Q') {
+ cond = COND_EQ;
+ }
+ break;
+
+ case 'F':
+ if (s[1] == 'L') {
+ cond = COND_FL;
+ }
+ break;
+
+ case 'G':
+ if (s[1] == 'E') {
+ cond = COND_GE;
+ } else if (s[1] == 'T') {
+ cond = COND_GT;
+ }
+ break;
+
+ case 'L':
+ if (s[1] == 'E') {
+ cond = COND_LE;
+ } else if (s[1] == 'T') {
+ cond = COND_LT;
+ }
+ break;
+
+ case 'N':
+ if (s[1] == 'E') {
+ cond = COND_NE;
+ }
+ break;
+
+ case 'T':
+ if (s[1] == 'R') {
+ cond = COND_TR;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return ((cond == 0) || (s[2] != '\0')) ? 0 : cond;
+}
+
+
+int
+_mesa_ARBvp_parse_option(struct asm_parser_state *state, const char *option)
+{
+ if (strcmp(option, "ARB_position_invariant") == 0) {
+ state->option.PositionInvariant = 1;
+ return 1;
+ }
+
+ return 0;
+}
+
+
+int
+_mesa_ARBfp_parse_option(struct asm_parser_state *state, const char *option)
+{
+ /* All of the options currently supported start with "ARB_". The code is
+ * currently structured with nested if-statements because eventually options
+ * that start with "NV_" will be supported. This structure will result in
+ * less churn when those options are added.
+ */
+ if (strncmp(option, "ARB_", 4) == 0) {
+ /* Advance the pointer past the "ARB_" prefix.
+ */
+ option += 4;
+
+
+ if (strncmp(option, "fog_", 4) == 0) {
+ option += 4;
+
+ if (state->option.Fog == OPTION_NONE) {
+ if (strcmp(option, "exp") == 0) {
+ state->option.Fog = OPTION_FOG_EXP;
+ return 1;
+ } else if (strcmp(option, "exp2") == 0) {
+ state->option.Fog = OPTION_FOG_EXP2;
+ return 1;
+ } else if (strcmp(option, "linear") == 0) {
+ state->option.Fog = OPTION_FOG_LINEAR;
+ return 1;
+ }
+ }
+
+ return 0;
+ } else if (strncmp(option, "precision_hint_", 15) == 0) {
+ option += 15;
+
+ if (state->option.PrecisionHint == OPTION_NONE) {
+ if (strcmp(option, "nicest") == 0) {
+ state->option.PrecisionHint = OPTION_NICEST;
+ return 1;
+ } else if (strcmp(option, "fastest") == 0) {
+ state->option.PrecisionHint = OPTION_FASTEST;
+ return 1;
+ }
+ }
+
+ return 0;
+ } else if (strcmp(option, "draw_buffers") == 0) {
+ /* Don't need to check extension availability because all Mesa-based
+ * drivers support GL_ARB_draw_buffers.
+ */
+ state->option.DrawBuffers = 1;
+ return 1;
+ } else if (strcmp(option, "fragment_program_shadow") == 0) {
+ if (state->ctx->Extensions.ARB_fragment_program_shadow) {
+ state->option.Shadow = 1;
+ return 1;
+ }
+ } else if (strncmp(option, "fragment_coord_", 15) == 0) {
+ option += 15;
+ if (state->ctx->Extensions.ARB_fragment_coord_conventions) {
+ if (strcmp(option, "origin_upper_left") == 0) {
+ state->option.OriginUpperLeft = 1;
+ return 1;
+ }
+ else if (strcmp(option, "pixel_center_integer") == 0) {
+ state->option.PixelCenterInteger = 1;
+ return 1;
+ }
+ }
+ }
+ } else if (strncmp(option, "ATI_", 4) == 0) {
+ option += 4;
+
+ if (strcmp(option, "draw_buffers") == 0) {
+ /* Don't need to check extension availability because all Mesa-based
+ * drivers support GL_ATI_draw_buffers.
+ */
+ state->option.DrawBuffers = 1;
+ return 1;
+ }
+ } else if (strncmp(option, "NV_fragment_program", 19) == 0) {
+ option += 19;
+
+ /* Other NV_fragment_program strings may be supported later.
+ */
+ if (option[0] == '\0') {
+ if (state->ctx->Extensions.NV_fragment_program_option) {
+ state->option.NV_fragment = 1;
+ return 1;
+ }
+ }
+ } else if (strncmp(option, "MESA_", 5) == 0) {
+ option += 5;
+
+ if (strcmp(option, "texture_array") == 0) {
+ if (state->ctx->Extensions.MESA_texture_array) {
+ state->option.TexArray = 1;
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/mesalib/src/mesa/program/program_parser.h b/mesalib/src/mesa/program/program_parser.h index 8e5aaee95..cbbb2677d 100644 --- a/mesalib/src/mesa/program/program_parser.h +++ b/mesalib/src/mesa/program/program_parser.h @@ -1,300 +1,300 @@ -/* - * Copyright © 2009 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. - */ -#pragma once - -#include "main/config.h" - -struct gl_context; - -enum asm_type { - at_none, - at_address, - at_attrib, - at_param, - at_temp, - at_output -}; - -struct asm_symbol { - struct asm_symbol *next; /**< List linkage for freeing. */ - const char *name; - enum asm_type type; - unsigned attrib_binding; - unsigned output_binding; /**< Output / result register number. */ - - /** - * One of PROGRAM_STATE_VAR, PROGRAM_LOCAL_PARAM, or PROGRAM_ENV_PARAM. - */ - unsigned param_binding_type; - - /** - * Offset into the program_parameter_list where the tokens representing our - * bound state (or constants) start. - */ - unsigned param_binding_begin; - - /** - * Constants put into the parameter list may be swizzled. This - * field contain's the symbol's swizzle. (SWIZZLE_X/Y/Z/W) - */ - unsigned param_binding_swizzle; - - /* This is how many entries in the program_parameter_list we take up - * with our state tokens or constants. Note that this is _not_ the same as - * the number of param registers we eventually use. - */ - unsigned param_binding_length; - - /** - * Index of the temp register assigned to this variable. - */ - unsigned temp_binding; - - /** - * Flag whether or not a PARAM is an array - */ - unsigned param_is_array:1; - - - /** - * Flag whether or not a PARAM array is accessed indirectly - */ - unsigned param_accessed_indirectly:1; - - - /** - * \brief Is first pass of parameter layout done with this variable? - * - * The parameter layout routine operates in two passes. This flag tracks - * whether or not the first pass has handled this variable. - * - * \sa _mesa_layout_parameters - */ - unsigned pass1_done:1; -}; - - -struct asm_vector { - unsigned count; - float data[4]; -}; - - -struct asm_swizzle_mask { - unsigned swizzle:12; - unsigned mask:4; -}; - - -struct asm_src_register { - struct prog_src_register Base; - - /** - * Symbol associated with indirect access to parameter arrays. - * - * If \c Base::RelAddr is 1, this will point to the symbol for the parameter - * that is being dereferenced. Further, \c Base::Index will be the offset - * from the address register being used. - */ - struct asm_symbol *Symbol; -}; - - -struct asm_instruction { - struct prog_instruction Base; - struct asm_instruction *next; - struct asm_src_register SrcReg[3]; -}; - - -struct asm_parser_state { - struct gl_context *ctx; - struct gl_program *prog; - - /** - * Per-program target limits - */ - struct gl_program_constants *limits; - - struct _mesa_symbol_table *st; - - /** - * Linked list of symbols - * - * This list is \b only used when cleaning up compiler state and freeing - * memory. - */ - struct asm_symbol *sym; - - /** - * State for the lexer. - */ - void *scanner; - - /** - * Linked list of instructions generated during parsing. - */ - /*@{*/ - struct asm_instruction *inst_head; - struct asm_instruction *inst_tail; - /*@}*/ - - - /** - * Selected limits copied from gl_constants - * - * These are limits from the GL context, but various bits in the program - * must be validated against these values. - */ - /*@{*/ - unsigned MaxTextureCoordUnits; - unsigned MaxTextureImageUnits; - unsigned MaxTextureUnits; - unsigned MaxClipPlanes; - unsigned MaxLights; - unsigned MaxProgramMatrices; - unsigned MaxDrawBuffers; - /*@}*/ - - /** - * Value to use in state vector accessors for environment and local - * parameters - */ - unsigned state_param_enum; - - - /** - * Input attributes bound to specific names - * - * This is only needed so that errors can be properly produced when - * multiple ATTRIB statements bind illegal combinations of vertex - * attributes. - */ - unsigned InputsBound; - - enum { - invalid_mode = 0, - ARB_vertex, - ARB_fragment - } mode; - - struct { - unsigned PositionInvariant:1; - unsigned Fog:2; - unsigned PrecisionHint:2; - unsigned DrawBuffers:1; - unsigned Shadow:1; - unsigned TexRect:1; - unsigned TexArray:1; - unsigned NV_fragment:1; - unsigned OriginUpperLeft:1; - unsigned PixelCenterInteger:1; - } option; - - struct { - unsigned UsesKill:1; - } fragment; -}; - -#define OPTION_NONE 0 -#define OPTION_FOG_EXP 1 -#define OPTION_FOG_EXP2 2 -#define OPTION_FOG_LINEAR 3 -#define OPTION_NICEST 1 -#define OPTION_FASTEST 2 - -typedef struct YYLTYPE { - int first_line; - int first_column; - int last_line; - int last_column; - int position; -} YYLTYPE; - -#define YYLTYPE_IS_DECLARED 1 -#define YYLTYPE_IS_TRIVIAL 1 - - -extern GLboolean _mesa_parse_arb_program(struct gl_context *ctx, GLenum target, - const GLubyte *str, GLsizei len, struct asm_parser_state *state); - - - -/* From program_lexer.l. */ -extern void _mesa_program_lexer_dtor(void *scanner); - -extern void _mesa_program_lexer_ctor(void **scanner, - struct asm_parser_state *state, const char *string, size_t len); - - -/** - *\name From program_parse_extra.c - */ -/*@{*/ - -/** - * Parses and processes an option string to an ARB vertex program - * - * \return - * Non-zero on success, zero on failure. - */ -extern int _mesa_ARBvp_parse_option(struct asm_parser_state *state, - const char *option); - -/** - * Parses and processes an option string to an ARB fragment program - * - * \return - * Non-zero on success, zero on failure. - */ -extern int _mesa_ARBfp_parse_option(struct asm_parser_state *state, - const char *option); - -/** - * Parses and processes instruction suffixes - * - * Instruction suffixes, such as \c _SAT, are processed. The relevant bits - * are set in \c inst. If suffixes are encountered that are either not known - * or not supported by the modes and options set in \c state, zero will be - * returned. - * - * \return - * Non-zero on success, zero on failure. - */ -extern int _mesa_parse_instruction_suffix(const struct asm_parser_state *state, - const char *suffix, struct prog_instruction *inst); - -/** - * Parses a condition code name - * - * The condition code names (e.g., \c LT, \c GT, \c NE) were added to assembly - * shaders with the \c GL_NV_fragment_program_option extension. This function - * converts a string representation into one of the \c COND_ macros. - * - * \return - * One of the \c COND_ macros defined in prog_instruction.h on success or zero - * on failure. - */ -extern int _mesa_parse_cc(const char *s); - -/*@}*/ +/*
+ * Copyright © 2009 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.
+ */
+#pragma once
+
+#include "main/config.h"
+
+struct gl_context;
+
+enum asm_type {
+ at_none,
+ at_address,
+ at_attrib,
+ at_param,
+ at_temp,
+ at_output
+};
+
+struct asm_symbol {
+ struct asm_symbol *next; /**< List linkage for freeing. */
+ const char *name;
+ enum asm_type type;
+ unsigned attrib_binding;
+ unsigned output_binding; /**< Output / result register number. */
+
+ /**
+ * One of PROGRAM_STATE_VAR, PROGRAM_LOCAL_PARAM, or PROGRAM_ENV_PARAM.
+ */
+ unsigned param_binding_type;
+
+ /**
+ * Offset into the program_parameter_list where the tokens representing our
+ * bound state (or constants) start.
+ */
+ unsigned param_binding_begin;
+
+ /**
+ * Constants put into the parameter list may be swizzled. This
+ * field contain's the symbol's swizzle. (SWIZZLE_X/Y/Z/W)
+ */
+ unsigned param_binding_swizzle;
+
+ /* This is how many entries in the program_parameter_list we take up
+ * with our state tokens or constants. Note that this is _not_ the same as
+ * the number of param registers we eventually use.
+ */
+ unsigned param_binding_length;
+
+ /**
+ * Index of the temp register assigned to this variable.
+ */
+ unsigned temp_binding;
+
+ /**
+ * Flag whether or not a PARAM is an array
+ */
+ unsigned param_is_array:1;
+
+
+ /**
+ * Flag whether or not a PARAM array is accessed indirectly
+ */
+ unsigned param_accessed_indirectly:1;
+
+
+ /**
+ * \brief Is first pass of parameter layout done with this variable?
+ *
+ * The parameter layout routine operates in two passes. This flag tracks
+ * whether or not the first pass has handled this variable.
+ *
+ * \sa _mesa_layout_parameters
+ */
+ unsigned pass1_done:1;
+};
+
+
+struct asm_vector {
+ unsigned count;
+ float data[4];
+};
+
+
+struct asm_swizzle_mask {
+ unsigned swizzle:12;
+ unsigned mask:4;
+};
+
+
+struct asm_src_register {
+ struct prog_src_register Base;
+
+ /**
+ * Symbol associated with indirect access to parameter arrays.
+ *
+ * If \c Base::RelAddr is 1, this will point to the symbol for the parameter
+ * that is being dereferenced. Further, \c Base::Index will be the offset
+ * from the address register being used.
+ */
+ struct asm_symbol *Symbol;
+};
+
+
+struct asm_instruction {
+ struct prog_instruction Base;
+ struct asm_instruction *next;
+ struct asm_src_register SrcReg[3];
+};
+
+
+struct asm_parser_state {
+ struct gl_context *ctx;
+ struct gl_program *prog;
+
+ /**
+ * Per-program target limits
+ */
+ struct gl_program_constants *limits;
+
+ struct _mesa_symbol_table *st;
+
+ /**
+ * Linked list of symbols
+ *
+ * This list is \b only used when cleaning up compiler state and freeing
+ * memory.
+ */
+ struct asm_symbol *sym;
+
+ /**
+ * State for the lexer.
+ */
+ void *scanner;
+
+ /**
+ * Linked list of instructions generated during parsing.
+ */
+ /*@{*/
+ struct asm_instruction *inst_head;
+ struct asm_instruction *inst_tail;
+ /*@}*/
+
+
+ /**
+ * Selected limits copied from gl_constants
+ *
+ * These are limits from the GL context, but various bits in the program
+ * must be validated against these values.
+ */
+ /*@{*/
+ unsigned MaxTextureCoordUnits;
+ unsigned MaxTextureImageUnits;
+ unsigned MaxTextureUnits;
+ unsigned MaxClipPlanes;
+ unsigned MaxLights;
+ unsigned MaxProgramMatrices;
+ unsigned MaxDrawBuffers;
+ /*@}*/
+
+ /**
+ * Value to use in state vector accessors for environment and local
+ * parameters
+ */
+ unsigned state_param_enum;
+
+
+ /**
+ * Input attributes bound to specific names
+ *
+ * This is only needed so that errors can be properly produced when
+ * multiple ATTRIB statements bind illegal combinations of vertex
+ * attributes.
+ */
+ unsigned InputsBound;
+
+ enum {
+ invalid_mode = 0,
+ ARB_vertex,
+ ARB_fragment
+ } mode;
+
+ struct {
+ unsigned PositionInvariant:1;
+ unsigned Fog:2;
+ unsigned PrecisionHint:2;
+ unsigned DrawBuffers:1;
+ unsigned Shadow:1;
+ unsigned TexRect:1;
+ unsigned TexArray:1;
+ unsigned NV_fragment:1;
+ unsigned OriginUpperLeft:1;
+ unsigned PixelCenterInteger:1;
+ } option;
+
+ struct {
+ unsigned UsesKill:1;
+ } fragment;
+};
+
+#define OPTION_NONE 0
+#define OPTION_FOG_EXP 1
+#define OPTION_FOG_EXP2 2
+#define OPTION_FOG_LINEAR 3
+#define OPTION_NICEST 1
+#define OPTION_FASTEST 2
+
+typedef struct YYLTYPE {
+ int first_line;
+ int first_column;
+ int last_line;
+ int last_column;
+ int position;
+} YYLTYPE;
+
+#define YYLTYPE_IS_DECLARED 1
+#define YYLTYPE_IS_TRIVIAL 1
+
+
+extern GLboolean _mesa_parse_arb_program(struct gl_context *ctx, GLenum target,
+ const GLubyte *str, GLsizei len, struct asm_parser_state *state);
+
+
+
+/* From program_lexer.l. */
+extern void _mesa_program_lexer_dtor(void *scanner);
+
+extern void _mesa_program_lexer_ctor(void **scanner,
+ struct asm_parser_state *state, const char *string, size_t len);
+
+
+/**
+ *\name From program_parse_extra.c
+ */
+/*@{*/
+
+/**
+ * Parses and processes an option string to an ARB vertex program
+ *
+ * \return
+ * Non-zero on success, zero on failure.
+ */
+extern int _mesa_ARBvp_parse_option(struct asm_parser_state *state,
+ const char *option);
+
+/**
+ * Parses and processes an option string to an ARB fragment program
+ *
+ * \return
+ * Non-zero on success, zero on failure.
+ */
+extern int _mesa_ARBfp_parse_option(struct asm_parser_state *state,
+ const char *option);
+
+/**
+ * Parses and processes instruction suffixes
+ *
+ * Instruction suffixes, such as \c _SAT, are processed. The relevant bits
+ * are set in \c inst. If suffixes are encountered that are either not known
+ * or not supported by the modes and options set in \c state, zero will be
+ * returned.
+ *
+ * \return
+ * Non-zero on success, zero on failure.
+ */
+extern int _mesa_parse_instruction_suffix(const struct asm_parser_state *state,
+ const char *suffix, struct prog_instruction *inst);
+
+/**
+ * Parses a condition code name
+ *
+ * The condition code names (e.g., \c LT, \c GT, \c NE) were added to assembly
+ * shaders with the \c GL_NV_fragment_program_option extension. This function
+ * converts a string representation into one of the \c COND_ macros.
+ *
+ * \return
+ * One of the \c COND_ macros defined in prog_instruction.h on success or zero
+ * on failure.
+ */
+extern int _mesa_parse_cc(const char *s);
+
+/*@}*/
diff --git a/mesalib/src/mesa/program/programopt.c b/mesalib/src/mesa/program/programopt.c index a239da2c6..a01252dab 100644 --- a/mesalib/src/mesa/program/programopt.c +++ b/mesalib/src/mesa/program/programopt.c @@ -1,679 +1,679 @@ -/* - * 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 instructions to implement fog - * - * The \c fragment.fogcoord input is used to compute the fog blend factor. - * - * \param ctx The GL context - * \param fprog Fragment program that fog instructions will be appended to. - * \param fog_mode Fog mode. One of \c GL_EXP, \c GL_EXP2, or \c GL_LINEAR. - * \param saturate True if writes to color outputs should be clamped to [0, 1] - * - * \note - * This function sets \c FRAG_BIT_FOGC in \c fprog->Base.InputsRead. - * - * \todo 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, GLenum fog_mode, - 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 (fog_mode == GL_NONE) { - _mesa_problem(ctx, "_mesa_append_fog_code() called for fragment program" - " with fog_mode == 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 (fog_mode == 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(fog_mode == GL_EXP || fog_mode == 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 - = (fog_mode == 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 (fog_mode == 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; -} - - - -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 instructions to implement fog
+ *
+ * The \c fragment.fogcoord input is used to compute the fog blend factor.
+ *
+ * \param ctx The GL context
+ * \param fprog Fragment program that fog instructions will be appended to.
+ * \param fog_mode Fog mode. One of \c GL_EXP, \c GL_EXP2, or \c GL_LINEAR.
+ * \param saturate True if writes to color outputs should be clamped to [0, 1]
+ *
+ * \note
+ * This function sets \c FRAG_BIT_FOGC in \c fprog->Base.InputsRead.
+ *
+ * \todo 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, GLenum fog_mode,
+ 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 (fog_mode == GL_NONE) {
+ _mesa_problem(ctx, "_mesa_append_fog_code() called for fragment program"
+ " with fog_mode == 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 (fog_mode == 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(fog_mode == GL_EXP || fog_mode == 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
+ = (fog_mode == 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 (fog_mode == 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;
+}
+
+
+
+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 b9205823c..99d7c3757 100644 --- a/mesalib/src/mesa/program/programopt.h +++ b/mesalib/src/mesa/program/programopt.h @@ -1,55 +1,55 @@ -/* - * 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, GLenum fog_mode, - 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 */ +/*
+ * 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, GLenum fog_mode,
+ 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 */
diff --git a/mesalib/src/mesa/program/register_allocate.c b/mesalib/src/mesa/program/register_allocate.c index de96eb42c..095d53796 100644 --- a/mesalib/src/mesa/program/register_allocate.c +++ b/mesalib/src/mesa/program/register_allocate.c @@ -1,537 +1,537 @@ -/* - * 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. - * - * Authors: - * Eric Anholt <eric@anholt.net> - * - */ - -/** @file register_allocate.c - * - * Graph-coloring register allocator. - * - * The basic idea of graph coloring is to make a node in a graph for - * every thing that needs a register (color) number assigned, and make - * edges in the graph between nodes that interfere (can't be allocated - * to the same register at the same time). - * - * During the "simplify" process, any any node with fewer edges than - * there are registers means that that edge can get assigned a - * register regardless of what its neighbors choose, so that node is - * pushed on a stack and removed (with its edges) from the graph. - * That likely causes other nodes to become trivially colorable as well. - * - * Then during the "select" process, nodes are popped off of that - * stack, their edges restored, and assigned a color different from - * their neighbors. Because they were pushed on the stack only when - * they were trivially colorable, any color chosen won't interfere - * with the registers to be popped later. - * - * The downside to most graph coloring is that real hardware often has - * limitations, like registers that need to be allocated to a node in - * pairs, or aligned on some boundary. This implementation follows - * the paper "Retargetable Graph-Coloring Register Allocation for - * Irregular Architectures" by Johan Runeson and Sven-Olof Nyström. - * - * In this system, there are register classes each containing various - * registers, and registers may interfere with other registers. For - * example, one might have a class of base registers, and a class of - * aligned register pairs that would each interfere with their pair of - * the base registers. Each node has a register class it needs to be - * assigned to. Define p(B) to be the size of register class B, and - * q(B,C) to be the number of registers in B that the worst choice - * register in C could conflict with. Then, this system replaces the - * basic graph coloring test of "fewer edges from this node than there - * are registers" with "For this node of class B, the sum of q(B,C) - * for each neighbor node of class C is less than pB". - * - * A nice feature of the pq test is that q(B,C) can be computed once - * up front and stored in a 2-dimensional array, so that the cost of - * coloring a node is constant with the number of registers. We do - * this during ra_set_finalize(). - */ - -#include <ralloc.h> - -#include "main/imports.h" -#include "main/macros.h" -#include "main/mtypes.h" -#include "register_allocate.h" - -#define NO_REG ~0 - -struct ra_reg { - GLboolean *conflicts; - unsigned int *conflict_list; - unsigned int conflict_list_size; - unsigned int num_conflicts; -}; - -struct ra_regs { - struct ra_reg *regs; - unsigned int count; - - struct ra_class **classes; - unsigned int class_count; -}; - -struct ra_class { - GLboolean *regs; - - /** - * p(B) in Runeson/Nyström paper. - * - * This is "how many regs are in the set." - */ - unsigned int p; - - /** - * q(B,C) (indexed by C, B is this register class) in - * Runeson/Nyström paper. This is "how many registers of B could - * the worst choice register from C conflict with". - */ - unsigned int *q; -}; - -struct ra_node { - /** @{ - * - * List of which nodes this node interferes with. This should be - * symmetric with the other node. - */ - GLboolean *adjacency; - unsigned int *adjacency_list; - unsigned int adjacency_count; - /** @} */ - - unsigned int class; - - /* Register, if assigned, or NO_REG. */ - unsigned int reg; - - /** - * Set when the node is in the trivially colorable stack. When - * set, the adjacency to this node is ignored, to implement the - * "remove the edge from the graph" in simplification without - * having to actually modify the adjacency_list. - */ - GLboolean in_stack; - - /* For an implementation that needs register spilling, this is the - * approximate cost of spilling this node. - */ - float spill_cost; -}; - -struct ra_graph { - struct ra_regs *regs; - /** - * the variables that need register allocation. - */ - struct ra_node *nodes; - unsigned int count; /**< count of nodes. */ - - unsigned int *stack; - unsigned int stack_count; -}; - -struct ra_regs * -ra_alloc_reg_set(unsigned int count) -{ - unsigned int i; - struct ra_regs *regs; - - regs = rzalloc(NULL, struct ra_regs); - regs->count = count; - regs->regs = rzalloc_array(regs, struct ra_reg, count); - - for (i = 0; i < count; i++) { - regs->regs[i].conflicts = rzalloc_array(regs->regs, GLboolean, count); - regs->regs[i].conflicts[i] = GL_TRUE; - - regs->regs[i].conflict_list = ralloc_array(regs->regs, unsigned int, 4); - regs->regs[i].conflict_list_size = 4; - regs->regs[i].conflict_list[0] = i; - regs->regs[i].num_conflicts = 1; - } - - return regs; -} - -static void -ra_add_conflict_list(struct ra_regs *regs, unsigned int r1, unsigned int r2) -{ - struct ra_reg *reg1 = ®s->regs[r1]; - - if (reg1->conflict_list_size == reg1->num_conflicts) { - reg1->conflict_list_size *= 2; - reg1->conflict_list = reralloc(regs->regs, reg1->conflict_list, - unsigned int, reg1->conflict_list_size); - } - reg1->conflict_list[reg1->num_conflicts++] = r2; - reg1->conflicts[r2] = GL_TRUE; -} - -void -ra_add_reg_conflict(struct ra_regs *regs, unsigned int r1, unsigned int r2) -{ - if (!regs->regs[r1].conflicts[r2]) { - ra_add_conflict_list(regs, r1, r2); - ra_add_conflict_list(regs, r2, r1); - } -} - -unsigned int -ra_alloc_reg_class(struct ra_regs *regs) -{ - struct ra_class *class; - - regs->classes = reralloc(regs->regs, regs->classes, struct ra_class *, - regs->class_count + 1); - - class = rzalloc(regs, struct ra_class); - regs->classes[regs->class_count] = class; - - class->regs = rzalloc_array(class, GLboolean, regs->count); - - return regs->class_count++; -} - -void -ra_class_add_reg(struct ra_regs *regs, unsigned int c, unsigned int r) -{ - struct ra_class *class = regs->classes[c]; - - class->regs[r] = GL_TRUE; - class->p++; -} - -/** - * Must be called after all conflicts and register classes have been - * set up and before the register set is used for allocation. - */ -void -ra_set_finalize(struct ra_regs *regs) -{ - unsigned int b, c; - - for (b = 0; b < regs->class_count; b++) { - regs->classes[b]->q = ralloc_array(regs, unsigned int, regs->class_count); - } - - /* Compute, for each class B and C, how many regs of B an - * allocation to C could conflict with. - */ - for (b = 0; b < regs->class_count; b++) { - for (c = 0; c < regs->class_count; c++) { - unsigned int rc; - int max_conflicts = 0; - - for (rc = 0; rc < regs->count; rc++) { - int conflicts = 0; - int i; - - if (!regs->classes[c]->regs[rc]) - continue; - - for (i = 0; i < regs->regs[rc].num_conflicts; i++) { - unsigned int rb = regs->regs[rc].conflict_list[i]; - if (regs->classes[b]->regs[rb]) - conflicts++; - } - max_conflicts = MAX2(max_conflicts, conflicts); - } - regs->classes[b]->q[c] = max_conflicts; - } - } -} - -static void -ra_add_node_adjacency(struct ra_graph *g, unsigned int n1, unsigned int n2) -{ - g->nodes[n1].adjacency[n2] = GL_TRUE; - g->nodes[n1].adjacency_list[g->nodes[n1].adjacency_count] = n2; - g->nodes[n1].adjacency_count++; -} - -struct ra_graph * -ra_alloc_interference_graph(struct ra_regs *regs, unsigned int count) -{ - struct ra_graph *g; - unsigned int i; - - g = rzalloc(regs, struct ra_graph); - g->regs = regs; - g->nodes = rzalloc_array(g, struct ra_node, count); - g->count = count; - - g->stack = rzalloc_array(g, unsigned int, count); - - for (i = 0; i < count; i++) { - g->nodes[i].adjacency = rzalloc_array(g, GLboolean, count); - g->nodes[i].adjacency_list = ralloc_array(g, unsigned int, count); - g->nodes[i].adjacency_count = 0; - ra_add_node_adjacency(g, i, i); - g->nodes[i].reg = NO_REG; - } - - return g; -} - -void -ra_set_node_class(struct ra_graph *g, - unsigned int n, unsigned int class) -{ - g->nodes[n].class = class; -} - -void -ra_add_node_interference(struct ra_graph *g, - unsigned int n1, unsigned int n2) -{ - if (!g->nodes[n1].adjacency[n2]) { - ra_add_node_adjacency(g, n1, n2); - ra_add_node_adjacency(g, n2, n1); - } -} - -static GLboolean pq_test(struct ra_graph *g, unsigned int n) -{ - unsigned int j; - unsigned int q = 0; - int n_class = g->nodes[n].class; - - for (j = 0; j < g->nodes[n].adjacency_count; j++) { - unsigned int n2 = g->nodes[n].adjacency_list[j]; - unsigned int n2_class = g->nodes[n2].class; - - if (n != n2 && !g->nodes[n2].in_stack) { - q += g->regs->classes[n_class]->q[n2_class]; - } - } - - return q < g->regs->classes[n_class]->p; -} - -/** - * Simplifies the interference graph by pushing all - * trivially-colorable nodes into a stack of nodes to be colored, - * removing them from the graph, and rinsing and repeating. - * - * Returns GL_TRUE if all nodes were removed from the graph. GL_FALSE - * means that either spilling will be required, or optimistic coloring - * should be applied. - */ -GLboolean -ra_simplify(struct ra_graph *g) -{ - GLboolean progress = GL_TRUE; - int i; - - while (progress) { - progress = GL_FALSE; - - for (i = g->count - 1; i >= 0; i--) { - if (g->nodes[i].in_stack || g->nodes[i].reg != NO_REG) - continue; - - if (pq_test(g, i)) { - g->stack[g->stack_count] = i; - g->stack_count++; - g->nodes[i].in_stack = GL_TRUE; - progress = GL_TRUE; - } - } - } - - for (i = 0; i < g->count; i++) { - if (!g->nodes[i].in_stack) - return GL_FALSE; - } - - return GL_TRUE; -} - -/** - * Pops nodes from the stack back into the graph, coloring them with - * registers as they go. - * - * If all nodes were trivially colorable, then this must succeed. If - * not (optimistic coloring), then it may return GL_FALSE; - */ -GLboolean -ra_select(struct ra_graph *g) -{ - int i; - - while (g->stack_count != 0) { - unsigned int r; - int n = g->stack[g->stack_count - 1]; - struct ra_class *c = g->regs->classes[g->nodes[n].class]; - - /* Find the lowest-numbered reg which is not used by a member - * of the graph adjacent to us. - */ - for (r = 0; r < g->regs->count; r++) { - if (!c->regs[r]) - continue; - - /* Check if any of our neighbors conflict with this register choice. */ - for (i = 0; i < g->nodes[n].adjacency_count; i++) { - unsigned int n2 = g->nodes[n].adjacency_list[i]; - - if (!g->nodes[n2].in_stack && - g->regs->regs[r].conflicts[g->nodes[n2].reg]) { - break; - } - } - if (i == g->nodes[n].adjacency_count) - break; - } - if (r == g->regs->count) - return GL_FALSE; - - g->nodes[n].reg = r; - g->nodes[n].in_stack = GL_FALSE; - g->stack_count--; - } - - return GL_TRUE; -} - -/** - * Optimistic register coloring: Just push the remaining nodes - * on the stack. They'll be colored first in ra_select(), and - * if they succeed then the locally-colorable nodes are still - * locally-colorable and the rest of the register allocation - * will succeed. - */ -void -ra_optimistic_color(struct ra_graph *g) -{ - unsigned int i; - - for (i = 0; i < g->count; i++) { - if (g->nodes[i].in_stack || g->nodes[i].reg != NO_REG) - continue; - - g->stack[g->stack_count] = i; - g->stack_count++; - g->nodes[i].in_stack = GL_TRUE; - } -} - -GLboolean -ra_allocate_no_spills(struct ra_graph *g) -{ - if (!ra_simplify(g)) { - ra_optimistic_color(g); - } - return ra_select(g); -} - -unsigned int -ra_get_node_reg(struct ra_graph *g, unsigned int n) -{ - return g->nodes[n].reg; -} - -/** - * Forces a node to a specific register. This can be used to avoid - * creating a register class containing one node when handling data - * that must live in a fixed location and is known to not conflict - * with other forced register assignment (as is common with shader - * input data). These nodes do not end up in the stack during - * ra_simplify(), and thus at ra_select() time it is as if they were - * the first popped off the stack and assigned their fixed locations. - * - * Must be called before ra_simplify(). - */ -void -ra_set_node_reg(struct ra_graph *g, unsigned int n, unsigned int reg) -{ - g->nodes[n].reg = reg; - g->nodes[n].in_stack = GL_FALSE; -} - -static float -ra_get_spill_benefit(struct ra_graph *g, unsigned int n) -{ - int j; - float benefit = 0; - int n_class = g->nodes[n].class; - - /* Define the benefit of eliminating an interference between n, n2 - * through spilling as q(C, B) / p(C). This is similar to the - * "count number of edges" approach of traditional graph coloring, - * but takes classes into account. - */ - for (j = 0; j < g->nodes[n].adjacency_count; j++) { - unsigned int n2 = g->nodes[n].adjacency_list[j]; - if (n != n2) { - unsigned int n2_class = g->nodes[n2].class; - benefit += ((float)g->regs->classes[n_class]->q[n2_class] / - g->regs->classes[n_class]->p); - } - } - - return benefit; -} - -/** - * Returns a node number to be spilled according to the cost/benefit using - * the pq test, or -1 if there are no spillable nodes. - */ -int -ra_get_best_spill_node(struct ra_graph *g) -{ - unsigned int best_node = -1; - unsigned int best_benefit = 0.0; - unsigned int n; - - for (n = 0; n < g->count; n++) { - float cost = g->nodes[n].spill_cost; - float benefit; - - if (cost <= 0.0) - continue; - - benefit = ra_get_spill_benefit(g, n); - - if (benefit / cost > best_benefit) { - best_benefit = benefit / cost; - best_node = n; - } - } - - return best_node; -} - -/** - * Only nodes with a spill cost set (cost != 0.0) will be considered - * for register spilling. - */ -void -ra_set_node_spill_cost(struct ra_graph *g, unsigned int n, float cost) -{ - g->nodes[n].spill_cost = cost; -} +/*
+ * 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.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ *
+ */
+
+/** @file register_allocate.c
+ *
+ * Graph-coloring register allocator.
+ *
+ * The basic idea of graph coloring is to make a node in a graph for
+ * every thing that needs a register (color) number assigned, and make
+ * edges in the graph between nodes that interfere (can't be allocated
+ * to the same register at the same time).
+ *
+ * During the "simplify" process, any any node with fewer edges than
+ * there are registers means that that edge can get assigned a
+ * register regardless of what its neighbors choose, so that node is
+ * pushed on a stack and removed (with its edges) from the graph.
+ * That likely causes other nodes to become trivially colorable as well.
+ *
+ * Then during the "select" process, nodes are popped off of that
+ * stack, their edges restored, and assigned a color different from
+ * their neighbors. Because they were pushed on the stack only when
+ * they were trivially colorable, any color chosen won't interfere
+ * with the registers to be popped later.
+ *
+ * The downside to most graph coloring is that real hardware often has
+ * limitations, like registers that need to be allocated to a node in
+ * pairs, or aligned on some boundary. This implementation follows
+ * the paper "Retargetable Graph-Coloring Register Allocation for
+ * Irregular Architectures" by Johan Runeson and Sven-Olof Nyström.
+ *
+ * In this system, there are register classes each containing various
+ * registers, and registers may interfere with other registers. For
+ * example, one might have a class of base registers, and a class of
+ * aligned register pairs that would each interfere with their pair of
+ * the base registers. Each node has a register class it needs to be
+ * assigned to. Define p(B) to be the size of register class B, and
+ * q(B,C) to be the number of registers in B that the worst choice
+ * register in C could conflict with. Then, this system replaces the
+ * basic graph coloring test of "fewer edges from this node than there
+ * are registers" with "For this node of class B, the sum of q(B,C)
+ * for each neighbor node of class C is less than pB".
+ *
+ * A nice feature of the pq test is that q(B,C) can be computed once
+ * up front and stored in a 2-dimensional array, so that the cost of
+ * coloring a node is constant with the number of registers. We do
+ * this during ra_set_finalize().
+ */
+
+#include <ralloc.h>
+
+#include "main/imports.h"
+#include "main/macros.h"
+#include "main/mtypes.h"
+#include "register_allocate.h"
+
+#define NO_REG ~0
+
+struct ra_reg {
+ GLboolean *conflicts;
+ unsigned int *conflict_list;
+ unsigned int conflict_list_size;
+ unsigned int num_conflicts;
+};
+
+struct ra_regs {
+ struct ra_reg *regs;
+ unsigned int count;
+
+ struct ra_class **classes;
+ unsigned int class_count;
+};
+
+struct ra_class {
+ GLboolean *regs;
+
+ /**
+ * p(B) in Runeson/Nyström paper.
+ *
+ * This is "how many regs are in the set."
+ */
+ unsigned int p;
+
+ /**
+ * q(B,C) (indexed by C, B is this register class) in
+ * Runeson/Nyström paper. This is "how many registers of B could
+ * the worst choice register from C conflict with".
+ */
+ unsigned int *q;
+};
+
+struct ra_node {
+ /** @{
+ *
+ * List of which nodes this node interferes with. This should be
+ * symmetric with the other node.
+ */
+ GLboolean *adjacency;
+ unsigned int *adjacency_list;
+ unsigned int adjacency_count;
+ /** @} */
+
+ unsigned int class;
+
+ /* Register, if assigned, or NO_REG. */
+ unsigned int reg;
+
+ /**
+ * Set when the node is in the trivially colorable stack. When
+ * set, the adjacency to this node is ignored, to implement the
+ * "remove the edge from the graph" in simplification without
+ * having to actually modify the adjacency_list.
+ */
+ GLboolean in_stack;
+
+ /* For an implementation that needs register spilling, this is the
+ * approximate cost of spilling this node.
+ */
+ float spill_cost;
+};
+
+struct ra_graph {
+ struct ra_regs *regs;
+ /**
+ * the variables that need register allocation.
+ */
+ struct ra_node *nodes;
+ unsigned int count; /**< count of nodes. */
+
+ unsigned int *stack;
+ unsigned int stack_count;
+};
+
+struct ra_regs *
+ra_alloc_reg_set(unsigned int count)
+{
+ unsigned int i;
+ struct ra_regs *regs;
+
+ regs = rzalloc(NULL, struct ra_regs);
+ regs->count = count;
+ regs->regs = rzalloc_array(regs, struct ra_reg, count);
+
+ for (i = 0; i < count; i++) {
+ regs->regs[i].conflicts = rzalloc_array(regs->regs, GLboolean, count);
+ regs->regs[i].conflicts[i] = GL_TRUE;
+
+ regs->regs[i].conflict_list = ralloc_array(regs->regs, unsigned int, 4);
+ regs->regs[i].conflict_list_size = 4;
+ regs->regs[i].conflict_list[0] = i;
+ regs->regs[i].num_conflicts = 1;
+ }
+
+ return regs;
+}
+
+static void
+ra_add_conflict_list(struct ra_regs *regs, unsigned int r1, unsigned int r2)
+{
+ struct ra_reg *reg1 = ®s->regs[r1];
+
+ if (reg1->conflict_list_size == reg1->num_conflicts) {
+ reg1->conflict_list_size *= 2;
+ reg1->conflict_list = reralloc(regs->regs, reg1->conflict_list,
+ unsigned int, reg1->conflict_list_size);
+ }
+ reg1->conflict_list[reg1->num_conflicts++] = r2;
+ reg1->conflicts[r2] = GL_TRUE;
+}
+
+void
+ra_add_reg_conflict(struct ra_regs *regs, unsigned int r1, unsigned int r2)
+{
+ if (!regs->regs[r1].conflicts[r2]) {
+ ra_add_conflict_list(regs, r1, r2);
+ ra_add_conflict_list(regs, r2, r1);
+ }
+}
+
+unsigned int
+ra_alloc_reg_class(struct ra_regs *regs)
+{
+ struct ra_class *class;
+
+ regs->classes = reralloc(regs->regs, regs->classes, struct ra_class *,
+ regs->class_count + 1);
+
+ class = rzalloc(regs, struct ra_class);
+ regs->classes[regs->class_count] = class;
+
+ class->regs = rzalloc_array(class, GLboolean, regs->count);
+
+ return regs->class_count++;
+}
+
+void
+ra_class_add_reg(struct ra_regs *regs, unsigned int c, unsigned int r)
+{
+ struct ra_class *class = regs->classes[c];
+
+ class->regs[r] = GL_TRUE;
+ class->p++;
+}
+
+/**
+ * Must be called after all conflicts and register classes have been
+ * set up and before the register set is used for allocation.
+ */
+void
+ra_set_finalize(struct ra_regs *regs)
+{
+ unsigned int b, c;
+
+ for (b = 0; b < regs->class_count; b++) {
+ regs->classes[b]->q = ralloc_array(regs, unsigned int, regs->class_count);
+ }
+
+ /* Compute, for each class B and C, how many regs of B an
+ * allocation to C could conflict with.
+ */
+ for (b = 0; b < regs->class_count; b++) {
+ for (c = 0; c < regs->class_count; c++) {
+ unsigned int rc;
+ int max_conflicts = 0;
+
+ for (rc = 0; rc < regs->count; rc++) {
+ int conflicts = 0;
+ int i;
+
+ if (!regs->classes[c]->regs[rc])
+ continue;
+
+ for (i = 0; i < regs->regs[rc].num_conflicts; i++) {
+ unsigned int rb = regs->regs[rc].conflict_list[i];
+ if (regs->classes[b]->regs[rb])
+ conflicts++;
+ }
+ max_conflicts = MAX2(max_conflicts, conflicts);
+ }
+ regs->classes[b]->q[c] = max_conflicts;
+ }
+ }
+}
+
+static void
+ra_add_node_adjacency(struct ra_graph *g, unsigned int n1, unsigned int n2)
+{
+ g->nodes[n1].adjacency[n2] = GL_TRUE;
+ g->nodes[n1].adjacency_list[g->nodes[n1].adjacency_count] = n2;
+ g->nodes[n1].adjacency_count++;
+}
+
+struct ra_graph *
+ra_alloc_interference_graph(struct ra_regs *regs, unsigned int count)
+{
+ struct ra_graph *g;
+ unsigned int i;
+
+ g = rzalloc(regs, struct ra_graph);
+ g->regs = regs;
+ g->nodes = rzalloc_array(g, struct ra_node, count);
+ g->count = count;
+
+ g->stack = rzalloc_array(g, unsigned int, count);
+
+ for (i = 0; i < count; i++) {
+ g->nodes[i].adjacency = rzalloc_array(g, GLboolean, count);
+ g->nodes[i].adjacency_list = ralloc_array(g, unsigned int, count);
+ g->nodes[i].adjacency_count = 0;
+ ra_add_node_adjacency(g, i, i);
+ g->nodes[i].reg = NO_REG;
+ }
+
+ return g;
+}
+
+void
+ra_set_node_class(struct ra_graph *g,
+ unsigned int n, unsigned int class)
+{
+ g->nodes[n].class = class;
+}
+
+void
+ra_add_node_interference(struct ra_graph *g,
+ unsigned int n1, unsigned int n2)
+{
+ if (!g->nodes[n1].adjacency[n2]) {
+ ra_add_node_adjacency(g, n1, n2);
+ ra_add_node_adjacency(g, n2, n1);
+ }
+}
+
+static GLboolean pq_test(struct ra_graph *g, unsigned int n)
+{
+ unsigned int j;
+ unsigned int q = 0;
+ int n_class = g->nodes[n].class;
+
+ for (j = 0; j < g->nodes[n].adjacency_count; j++) {
+ unsigned int n2 = g->nodes[n].adjacency_list[j];
+ unsigned int n2_class = g->nodes[n2].class;
+
+ if (n != n2 && !g->nodes[n2].in_stack) {
+ q += g->regs->classes[n_class]->q[n2_class];
+ }
+ }
+
+ return q < g->regs->classes[n_class]->p;
+}
+
+/**
+ * Simplifies the interference graph by pushing all
+ * trivially-colorable nodes into a stack of nodes to be colored,
+ * removing them from the graph, and rinsing and repeating.
+ *
+ * Returns GL_TRUE if all nodes were removed from the graph. GL_FALSE
+ * means that either spilling will be required, or optimistic coloring
+ * should be applied.
+ */
+GLboolean
+ra_simplify(struct ra_graph *g)
+{
+ GLboolean progress = GL_TRUE;
+ int i;
+
+ while (progress) {
+ progress = GL_FALSE;
+
+ for (i = g->count - 1; i >= 0; i--) {
+ if (g->nodes[i].in_stack || g->nodes[i].reg != NO_REG)
+ continue;
+
+ if (pq_test(g, i)) {
+ g->stack[g->stack_count] = i;
+ g->stack_count++;
+ g->nodes[i].in_stack = GL_TRUE;
+ progress = GL_TRUE;
+ }
+ }
+ }
+
+ for (i = 0; i < g->count; i++) {
+ if (!g->nodes[i].in_stack)
+ return GL_FALSE;
+ }
+
+ return GL_TRUE;
+}
+
+/**
+ * Pops nodes from the stack back into the graph, coloring them with
+ * registers as they go.
+ *
+ * If all nodes were trivially colorable, then this must succeed. If
+ * not (optimistic coloring), then it may return GL_FALSE;
+ */
+GLboolean
+ra_select(struct ra_graph *g)
+{
+ int i;
+
+ while (g->stack_count != 0) {
+ unsigned int r;
+ int n = g->stack[g->stack_count - 1];
+ struct ra_class *c = g->regs->classes[g->nodes[n].class];
+
+ /* Find the lowest-numbered reg which is not used by a member
+ * of the graph adjacent to us.
+ */
+ for (r = 0; r < g->regs->count; r++) {
+ if (!c->regs[r])
+ continue;
+
+ /* Check if any of our neighbors conflict with this register choice. */
+ for (i = 0; i < g->nodes[n].adjacency_count; i++) {
+ unsigned int n2 = g->nodes[n].adjacency_list[i];
+
+ if (!g->nodes[n2].in_stack &&
+ g->regs->regs[r].conflicts[g->nodes[n2].reg]) {
+ break;
+ }
+ }
+ if (i == g->nodes[n].adjacency_count)
+ break;
+ }
+ if (r == g->regs->count)
+ return GL_FALSE;
+
+ g->nodes[n].reg = r;
+ g->nodes[n].in_stack = GL_FALSE;
+ g->stack_count--;
+ }
+
+ return GL_TRUE;
+}
+
+/**
+ * Optimistic register coloring: Just push the remaining nodes
+ * on the stack. They'll be colored first in ra_select(), and
+ * if they succeed then the locally-colorable nodes are still
+ * locally-colorable and the rest of the register allocation
+ * will succeed.
+ */
+void
+ra_optimistic_color(struct ra_graph *g)
+{
+ unsigned int i;
+
+ for (i = 0; i < g->count; i++) {
+ if (g->nodes[i].in_stack || g->nodes[i].reg != NO_REG)
+ continue;
+
+ g->stack[g->stack_count] = i;
+ g->stack_count++;
+ g->nodes[i].in_stack = GL_TRUE;
+ }
+}
+
+GLboolean
+ra_allocate_no_spills(struct ra_graph *g)
+{
+ if (!ra_simplify(g)) {
+ ra_optimistic_color(g);
+ }
+ return ra_select(g);
+}
+
+unsigned int
+ra_get_node_reg(struct ra_graph *g, unsigned int n)
+{
+ return g->nodes[n].reg;
+}
+
+/**
+ * Forces a node to a specific register. This can be used to avoid
+ * creating a register class containing one node when handling data
+ * that must live in a fixed location and is known to not conflict
+ * with other forced register assignment (as is common with shader
+ * input data). These nodes do not end up in the stack during
+ * ra_simplify(), and thus at ra_select() time it is as if they were
+ * the first popped off the stack and assigned their fixed locations.
+ *
+ * Must be called before ra_simplify().
+ */
+void
+ra_set_node_reg(struct ra_graph *g, unsigned int n, unsigned int reg)
+{
+ g->nodes[n].reg = reg;
+ g->nodes[n].in_stack = GL_FALSE;
+}
+
+static float
+ra_get_spill_benefit(struct ra_graph *g, unsigned int n)
+{
+ int j;
+ float benefit = 0;
+ int n_class = g->nodes[n].class;
+
+ /* Define the benefit of eliminating an interference between n, n2
+ * through spilling as q(C, B) / p(C). This is similar to the
+ * "count number of edges" approach of traditional graph coloring,
+ * but takes classes into account.
+ */
+ for (j = 0; j < g->nodes[n].adjacency_count; j++) {
+ unsigned int n2 = g->nodes[n].adjacency_list[j];
+ if (n != n2) {
+ unsigned int n2_class = g->nodes[n2].class;
+ benefit += ((float)g->regs->classes[n_class]->q[n2_class] /
+ g->regs->classes[n_class]->p);
+ }
+ }
+
+ return benefit;
+}
+
+/**
+ * Returns a node number to be spilled according to the cost/benefit using
+ * the pq test, or -1 if there are no spillable nodes.
+ */
+int
+ra_get_best_spill_node(struct ra_graph *g)
+{
+ unsigned int best_node = -1;
+ unsigned int best_benefit = 0.0;
+ unsigned int n;
+
+ for (n = 0; n < g->count; n++) {
+ float cost = g->nodes[n].spill_cost;
+ float benefit;
+
+ if (cost <= 0.0)
+ continue;
+
+ benefit = ra_get_spill_benefit(g, n);
+
+ if (benefit / cost > best_benefit) {
+ best_benefit = benefit / cost;
+ best_node = n;
+ }
+ }
+
+ return best_node;
+}
+
+/**
+ * Only nodes with a spill cost set (cost != 0.0) will be considered
+ * for register spilling.
+ */
+void
+ra_set_node_spill_cost(struct ra_graph *g, unsigned int n, float cost)
+{
+ g->nodes[n].spill_cost = cost;
+}
diff --git a/mesalib/src/mesa/program/register_allocate.h b/mesalib/src/mesa/program/register_allocate.h index 5b95833f3..1345debd0 100644 --- a/mesalib/src/mesa/program/register_allocate.h +++ b/mesalib/src/mesa/program/register_allocate.h @@ -1,72 +1,72 @@ -/* - * 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. - * - * Authors: - * Eric Anholt <eric@anholt.net> - * - */ - -struct ra_class; -struct ra_regs; - -/* @{ - * Register set setup. - * - * This should be done once at backend initializaion, as - * ra_set_finalize is O(r^2*c^2). The registers may be virtual - * registers, such as aligned register pairs that conflict with the - * two real registers from which they are composed. - */ -struct ra_regs *ra_alloc_reg_set(unsigned int count); -unsigned int ra_alloc_reg_class(struct ra_regs *regs); -void ra_add_reg_conflict(struct ra_regs *regs, - unsigned int r1, unsigned int r2); -void ra_class_add_reg(struct ra_regs *regs, unsigned int c, unsigned int reg); -void ra_set_finalize(struct ra_regs *regs); -/** @} */ - -/** @{ Interference graph setup. - * - * Each interference graph node is a virtual variable in the IL. It - * is up to the user to ra_set_node_class() for the virtual variable, - * and compute live ranges and ra_node_interfere() between conflicting - * live ranges. - */ -struct ra_graph *ra_alloc_interference_graph(struct ra_regs *regs, - unsigned int count); -void ra_set_node_class(struct ra_graph *g, unsigned int n, unsigned int c); -void ra_add_node_interference(struct ra_graph *g, - unsigned int n1, unsigned int n2); -/** @} */ - -/** @{ Graph-coloring register allocation */ -GLboolean ra_simplify(struct ra_graph *g); -void ra_optimistic_color(struct ra_graph *g); -GLboolean ra_select(struct ra_graph *g); -GLboolean ra_allocate_no_spills(struct ra_graph *g); - -unsigned int ra_get_node_reg(struct ra_graph *g, unsigned int n); -void ra_set_node_reg(struct ra_graph * g, unsigned int n, unsigned int reg); -void ra_set_node_spill_cost(struct ra_graph *g, unsigned int n, float cost); -int ra_get_best_spill_node(struct ra_graph *g); -/** @} */ - +/*
+ * 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.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ *
+ */
+
+struct ra_class;
+struct ra_regs;
+
+/* @{
+ * Register set setup.
+ *
+ * This should be done once at backend initializaion, as
+ * ra_set_finalize is O(r^2*c^2). The registers may be virtual
+ * registers, such as aligned register pairs that conflict with the
+ * two real registers from which they are composed.
+ */
+struct ra_regs *ra_alloc_reg_set(unsigned int count);
+unsigned int ra_alloc_reg_class(struct ra_regs *regs);
+void ra_add_reg_conflict(struct ra_regs *regs,
+ unsigned int r1, unsigned int r2);
+void ra_class_add_reg(struct ra_regs *regs, unsigned int c, unsigned int reg);
+void ra_set_finalize(struct ra_regs *regs);
+/** @} */
+
+/** @{ Interference graph setup.
+ *
+ * Each interference graph node is a virtual variable in the IL. It
+ * is up to the user to ra_set_node_class() for the virtual variable,
+ * and compute live ranges and ra_node_interfere() between conflicting
+ * live ranges.
+ */
+struct ra_graph *ra_alloc_interference_graph(struct ra_regs *regs,
+ unsigned int count);
+void ra_set_node_class(struct ra_graph *g, unsigned int n, unsigned int c);
+void ra_add_node_interference(struct ra_graph *g,
+ unsigned int n1, unsigned int n2);
+/** @} */
+
+/** @{ Graph-coloring register allocation */
+GLboolean ra_simplify(struct ra_graph *g);
+void ra_optimistic_color(struct ra_graph *g);
+GLboolean ra_select(struct ra_graph *g);
+GLboolean ra_allocate_no_spills(struct ra_graph *g);
+
+unsigned int ra_get_node_reg(struct ra_graph *g, unsigned int n);
+void ra_set_node_reg(struct ra_graph * g, unsigned int n, unsigned int reg);
+void ra_set_node_spill_cost(struct ra_graph *g, unsigned int n, float cost);
+int ra_get_best_spill_node(struct ra_graph *g);
+/** @} */
+
diff --git a/mesalib/src/mesa/program/sampler.cpp b/mesalib/src/mesa/program/sampler.cpp index 1457d1199..b219d7016 100644 --- a/mesalib/src/mesa/program/sampler.cpp +++ b/mesalib/src/mesa/program/sampler.cpp @@ -1,137 +1,137 @@ -/* - * 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. - */ - -#include "ir.h" -#include "glsl_types.h" -#include "ir_visitor.h" - -extern "C" { -#include "main/compiler.h" -#include "main/mtypes.h" -#include "program/prog_parameter.h" -} - -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; -} - -class get_sampler_name : public ir_hierarchical_visitor -{ -public: - get_sampler_name(ir_dereference *last, - struct gl_shader_program *shader_program) - { - this->mem_ctx = ralloc_context(NULL); - this->shader_program = shader_program; - this->name = NULL; - this->offset = 0; - this->last = last; - } - - ~get_sampler_name() - { - ralloc_free(this->mem_ctx); - } - - virtual ir_visitor_status visit(ir_dereference_variable *ir) - { - this->name = ir->var->name; - return visit_continue; - } - - virtual ir_visitor_status visit_leave(ir_dereference_record *ir) - { - this->name = ralloc_asprintf(mem_ctx, "%s.%s", name, ir->field); - return visit_continue; - } - - virtual ir_visitor_status visit_leave(ir_dereference_array *ir) - { - ir_constant *index = ir->array_index->as_constant(); - int i; - - if (index) { - i = index->value.i[0]; - } else { - /* GLSL 1.10 and 1.20 allowed variable sampler array indices, - * while GLSL 1.30 requires that the array indices be - * constant integer expressions. We don't expect any driver - * to actually work with a really variable array index, so - * all that would work would be an unrolled loop counter that ends - * up being constant above. - */ - ralloc_strcat(&shader_program->InfoLog, - "warning: Variable sampler array index unsupported.\n" - "This feature of the language was removed in GLSL 1.20 " - "and is unlikely to be supported for 1.10 in Mesa.\n"); - i = 0; - } - if (ir != last) { - this->name = ralloc_asprintf(mem_ctx, "%s[%d]", name, i); - } else { - offset = i; - } - return visit_continue; - } - - struct gl_shader_program *shader_program; - const char *name; - void *mem_ctx; - int offset; - ir_dereference *last; -}; - -extern "C" { -int -_mesa_get_sampler_uniform_value(class ir_dereference *sampler, - struct gl_shader_program *shader_program, - const struct gl_program *prog) -{ - get_sampler_name getname(sampler, shader_program); - - sampler->accept(&getname); - - GLint index = _mesa_lookup_parameter_index(prog->Parameters, -1, - getname.name); - - if (index < 0) { - fail_link(shader_program, - "failed to find sampler named %s.\n", getname.name); - return 0; - } - - index += getname.offset; - - return prog->Parameters->ParameterValues[index][0]; -} -} +/*
+ * 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.
+ */
+
+#include "ir.h"
+#include "glsl_types.h"
+#include "ir_visitor.h"
+
+extern "C" {
+#include "main/compiler.h"
+#include "main/mtypes.h"
+#include "program/prog_parameter.h"
+}
+
+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;
+}
+
+class get_sampler_name : public ir_hierarchical_visitor
+{
+public:
+ get_sampler_name(ir_dereference *last,
+ struct gl_shader_program *shader_program)
+ {
+ this->mem_ctx = ralloc_context(NULL);
+ this->shader_program = shader_program;
+ this->name = NULL;
+ this->offset = 0;
+ this->last = last;
+ }
+
+ ~get_sampler_name()
+ {
+ ralloc_free(this->mem_ctx);
+ }
+
+ virtual ir_visitor_status visit(ir_dereference_variable *ir)
+ {
+ this->name = ir->var->name;
+ return visit_continue;
+ }
+
+ virtual ir_visitor_status visit_leave(ir_dereference_record *ir)
+ {
+ this->name = ralloc_asprintf(mem_ctx, "%s.%s", name, ir->field);
+ return visit_continue;
+ }
+
+ virtual ir_visitor_status visit_leave(ir_dereference_array *ir)
+ {
+ ir_constant *index = ir->array_index->as_constant();
+ int i;
+
+ if (index) {
+ i = index->value.i[0];
+ } else {
+ /* GLSL 1.10 and 1.20 allowed variable sampler array indices,
+ * while GLSL 1.30 requires that the array indices be
+ * constant integer expressions. We don't expect any driver
+ * to actually work with a really variable array index, so
+ * all that would work would be an unrolled loop counter that ends
+ * up being constant above.
+ */
+ ralloc_strcat(&shader_program->InfoLog,
+ "warning: Variable sampler array index unsupported.\n"
+ "This feature of the language was removed in GLSL 1.20 "
+ "and is unlikely to be supported for 1.10 in Mesa.\n");
+ i = 0;
+ }
+ if (ir != last) {
+ this->name = ralloc_asprintf(mem_ctx, "%s[%d]", name, i);
+ } else {
+ offset = i;
+ }
+ return visit_continue;
+ }
+
+ struct gl_shader_program *shader_program;
+ const char *name;
+ void *mem_ctx;
+ int offset;
+ ir_dereference *last;
+};
+
+extern "C" {
+int
+_mesa_get_sampler_uniform_value(class ir_dereference *sampler,
+ struct gl_shader_program *shader_program,
+ const struct gl_program *prog)
+{
+ get_sampler_name getname(sampler, shader_program);
+
+ sampler->accept(&getname);
+
+ GLint index = _mesa_lookup_parameter_index(prog->Parameters, -1,
+ getname.name);
+
+ if (index < 0) {
+ fail_link(shader_program,
+ "failed to find sampler named %s.\n", getname.name);
+ return 0;
+ }
+
+ index += getname.offset;
+
+ return prog->Parameters->ParameterValues[index][0];
+}
+}
|