1836 lines
54 KiB
C
1836 lines
54 KiB
C
/*
|
|
* Mesa 3-D graphics library
|
|
* Version: 6.5
|
|
*
|
|
* Copyright (C) 1999-2005 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 nvfragparse.c
|
|
* NVIDIA fragment program parser.
|
|
* \author Brian Paul
|
|
*/
|
|
|
|
/*
|
|
* Regarding GL_NV_fragment_program:
|
|
*
|
|
* Portions of this software may use or implement intellectual
|
|
* property owned and licensed by NVIDIA Corporation. NVIDIA disclaims
|
|
* any and all warranties with respect to such intellectual property,
|
|
* including any use thereof or modifications thereto.
|
|
*/
|
|
|
|
#include "glheader.h"
|
|
#include "context.h"
|
|
#include "hash.h"
|
|
#include "imports.h"
|
|
#include "macros.h"
|
|
#include "mtypes.h"
|
|
#include "program_instruction.h"
|
|
#include "nvfragparse.h"
|
|
#include "nvprogram.h"
|
|
#include "program.h"
|
|
|
|
|
|
#define INPUT_1V 1
|
|
#define INPUT_2V 2
|
|
#define INPUT_3V 3
|
|
#define INPUT_1S 4
|
|
#define INPUT_2S 5
|
|
#define INPUT_CC 6
|
|
#define INPUT_1V_T 7 /* one source vector, plus textureId */
|
|
#define INPUT_3V_T 8 /* one source vector, plus textureId */
|
|
#define INPUT_NONE 9
|
|
#define INPUT_1V_S 10 /* a string and a vector register */
|
|
#define OUTPUT_V 20
|
|
#define OUTPUT_S 21
|
|
#define OUTPUT_NONE 22
|
|
|
|
/* IRIX defines some of these */
|
|
#undef _R
|
|
#undef _H
|
|
#undef _X
|
|
#undef _C
|
|
#undef _S
|
|
|
|
/* Optional suffixes */
|
|
#define _R FLOAT32 /* float */
|
|
#define _H FLOAT16 /* half-float */
|
|
#define _X FIXED12 /* fixed */
|
|
#define _C 0x08 /* set cond codes */
|
|
#define _S 0x10 /* saturate, clamp result to [0,1] */
|
|
|
|
struct instruction_pattern {
|
|
const char *name;
|
|
enum prog_opcode opcode;
|
|
GLuint inputs;
|
|
GLuint outputs;
|
|
GLuint suffixes;
|
|
};
|
|
|
|
static const struct instruction_pattern Instructions[] = {
|
|
{ "ADD", OPCODE_ADD, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
|
|
{ "COS", OPCODE_COS, INPUT_1S, OUTPUT_S, _R | _H | _C | _S },
|
|
{ "DDX", OPCODE_DDX, INPUT_1V, OUTPUT_V, _R | _H | _C | _S },
|
|
{ "DDY", OPCODE_DDY, INPUT_1V, OUTPUT_V, _R | _H | _C | _S },
|
|
{ "DP3", OPCODE_DP3, INPUT_2V, OUTPUT_S, _R | _H | _X | _C | _S },
|
|
{ "DP4", OPCODE_DP4, INPUT_2V, OUTPUT_S, _R | _H | _X | _C | _S },
|
|
{ "DST", OPCODE_DP4, INPUT_2V, OUTPUT_V, _R | _H | _C | _S },
|
|
{ "EX2", OPCODE_DP4, INPUT_1S, OUTPUT_S, _R | _H | _C | _S },
|
|
{ "FLR", OPCODE_FLR, INPUT_1V, OUTPUT_V, _R | _H | _X | _C | _S },
|
|
{ "FRC", OPCODE_FRC, INPUT_1V, OUTPUT_V, _R | _H | _X | _C | _S },
|
|
{ "KIL", OPCODE_KIL_NV, INPUT_CC, OUTPUT_NONE, 0 },
|
|
{ "LG2", OPCODE_LG2, INPUT_1S, OUTPUT_S, _R | _H | _C | _S },
|
|
{ "LIT", OPCODE_LIT, INPUT_1V, OUTPUT_V, _R | _H | _C | _S },
|
|
{ "LRP", OPCODE_LRP, INPUT_3V, OUTPUT_V, _R | _H | _X | _C | _S },
|
|
{ "MAD", OPCODE_MAD, INPUT_3V, OUTPUT_V, _R | _H | _X | _C | _S },
|
|
{ "MAX", OPCODE_MAX, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
|
|
{ "MIN", OPCODE_MIN, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
|
|
{ "MOV", OPCODE_MOV, INPUT_1V, OUTPUT_V, _R | _H | _X | _C | _S },
|
|
{ "MUL", OPCODE_MUL, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
|
|
{ "PK2H", OPCODE_PK2H, INPUT_1V, OUTPUT_S, 0 },
|
|
{ "PK2US", OPCODE_PK2US, INPUT_1V, OUTPUT_S, 0 },
|
|
{ "PK4B", OPCODE_PK4B, INPUT_1V, OUTPUT_S, 0 },
|
|
{ "PK4UB", OPCODE_PK4UB, INPUT_1V, OUTPUT_S, 0 },
|
|
{ "POW", OPCODE_POW, INPUT_2S, OUTPUT_S, _R | _H | _C | _S },
|
|
{ "RCP", OPCODE_RCP, INPUT_1S, OUTPUT_S, _R | _H | _C | _S },
|
|
{ "RFL", OPCODE_RFL, INPUT_2V, OUTPUT_V, _R | _H | _C | _S },
|
|
{ "RSQ", OPCODE_RSQ, INPUT_1S, OUTPUT_S, _R | _H | _C | _S },
|
|
{ "SEQ", OPCODE_SEQ, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
|
|
{ "SFL", OPCODE_SFL, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
|
|
{ "SGE", OPCODE_SGE, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
|
|
{ "SGT", OPCODE_SGT, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
|
|
{ "SIN", OPCODE_SIN, INPUT_1S, OUTPUT_S, _R | _H | _C | _S },
|
|
{ "SLE", OPCODE_SLE, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
|
|
{ "SLT", OPCODE_SLT, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
|
|
{ "SNE", OPCODE_SNE, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
|
|
{ "STR", OPCODE_STR, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
|
|
{ "SUB", OPCODE_SUB, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
|
|
{ "TEX", OPCODE_TEX, INPUT_1V_T, OUTPUT_V, _C | _S },
|
|
{ "TXD", OPCODE_TXD, INPUT_3V_T, OUTPUT_V, _C | _S },
|
|
{ "TXP", OPCODE_TXP_NV, INPUT_1V_T, OUTPUT_V, _C | _S },
|
|
{ "UP2H", OPCODE_UP2H, INPUT_1S, OUTPUT_V, _C | _S },
|
|
{ "UP2US", OPCODE_UP2US, INPUT_1S, OUTPUT_V, _C | _S },
|
|
{ "UP4B", OPCODE_UP4B, INPUT_1S, OUTPUT_V, _C | _S },
|
|
{ "UP4UB", OPCODE_UP4UB, INPUT_1S, OUTPUT_V, _C | _S },
|
|
{ "X2D", OPCODE_X2D, INPUT_3V, OUTPUT_V, _R | _H | _C | _S },
|
|
{ "PRINT", OPCODE_PRINT, INPUT_1V_S, OUTPUT_NONE, 0 },
|
|
{ NULL, (enum prog_opcode) -1, 0, 0, 0 }
|
|
};
|
|
|
|
|
|
/*
|
|
* Information needed or computed during parsing.
|
|
* Remember, we can't modify the target program object until we've
|
|
* _successfully_ parsed the program text.
|
|
*/
|
|
struct parse_state {
|
|
GLcontext *ctx;
|
|
const GLubyte *start; /* start of program string */
|
|
const GLubyte *pos; /* current position */
|
|
const GLubyte *curLine;
|
|
struct gl_fragment_program *program; /* current program */
|
|
|
|
struct gl_program_parameter_list *parameters;
|
|
|
|
GLuint numInst; /* number of instructions parsed */
|
|
GLuint inputsRead; /* bitmask of input registers used */
|
|
GLuint outputsWritten; /* bitmask of 1 << FRAG_OUTPUT_* bits */
|
|
GLuint texturesUsed[MAX_TEXTURE_IMAGE_UNITS];
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* Called whenever we find an error during parsing.
|
|
*/
|
|
static void
|
|
record_error(struct parse_state *parseState, const char *msg, int lineNo)
|
|
{
|
|
#ifdef DEBUG
|
|
GLint line, column;
|
|
const GLubyte *lineStr;
|
|
lineStr = _mesa_find_line_column(parseState->start,
|
|
parseState->pos, &line, &column);
|
|
_mesa_debug(parseState->ctx,
|
|
"nvfragparse.c(%d): line %d, column %d:%s (%s)\n",
|
|
lineNo, line, column, (char *) lineStr, msg);
|
|
_mesa_free((void *) lineStr);
|
|
#else
|
|
(void) lineNo;
|
|
#endif
|
|
|
|
/* Check that no error was already recorded. Only record the first one. */
|
|
if (parseState->ctx->Program.ErrorString[0] == 0) {
|
|
_mesa_set_program_error(parseState->ctx,
|
|
parseState->pos - parseState->start,
|
|
msg);
|
|
}
|
|
}
|
|
|
|
|
|
#define RETURN_ERROR \
|
|
do { \
|
|
record_error(parseState, "Unexpected end of input.", __LINE__); \
|
|
return GL_FALSE; \
|
|
} while(0)
|
|
|
|
#define RETURN_ERROR1(msg) \
|
|
do { \
|
|
record_error(parseState, msg, __LINE__); \
|
|
return GL_FALSE; \
|
|
} while(0)
|
|
|
|
#define RETURN_ERROR2(msg1, msg2) \
|
|
do { \
|
|
char err[1000]; \
|
|
_mesa_sprintf(err, "%s %s", msg1, msg2); \
|
|
record_error(parseState, err, __LINE__); \
|
|
return GL_FALSE; \
|
|
} while(0)
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Search a list of instruction structures for a match.
|
|
*/
|
|
static struct instruction_pattern
|
|
MatchInstruction(const GLubyte *token)
|
|
{
|
|
const struct instruction_pattern *inst;
|
|
struct instruction_pattern result;
|
|
|
|
for (inst = Instructions; inst->name; inst++) {
|
|
if (_mesa_strncmp((const char *) token, inst->name, 3) == 0) {
|
|
/* matched! */
|
|
int i = 3;
|
|
result = *inst;
|
|
result.suffixes = 0;
|
|
/* look at suffix */
|
|
if (token[i] == 'R') {
|
|
result.suffixes |= _R;
|
|
i++;
|
|
}
|
|
else if (token[i] == 'H') {
|
|
result.suffixes |= _H;
|
|
i++;
|
|
}
|
|
else if (token[i] == 'X') {
|
|
result.suffixes |= _X;
|
|
i++;
|
|
}
|
|
if (token[i] == 'C') {
|
|
result.suffixes |= _C;
|
|
i++;
|
|
}
|
|
if (token[i] == '_' && token[i+1] == 'S' &&
|
|
token[i+2] == 'A' && token[i+3] == 'T') {
|
|
result.suffixes |= _S;
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
result.opcode = MAX_OPCODE; /* i.e. invalid instruction */
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
|
|
/**********************************************************************/
|
|
|
|
|
|
static GLboolean IsLetter(GLubyte b)
|
|
{
|
|
return (b >= 'a' && b <= 'z') ||
|
|
(b >= 'A' && b <= 'Z') ||
|
|
(b == '_') ||
|
|
(b == '$');
|
|
}
|
|
|
|
|
|
static GLboolean IsDigit(GLubyte b)
|
|
{
|
|
return b >= '0' && b <= '9';
|
|
}
|
|
|
|
|
|
static GLboolean IsWhitespace(GLubyte b)
|
|
{
|
|
return b == ' ' || b == '\t' || b == '\n' || b == '\r';
|
|
}
|
|
|
|
|
|
/**
|
|
* Starting at 'str' find the next token. A token can be an integer,
|
|
* an identifier or punctuation symbol.
|
|
* \return <= 0 we found an error, else, return number of characters parsed.
|
|
*/
|
|
static GLint
|
|
GetToken(struct parse_state *parseState, GLubyte *token)
|
|
{
|
|
const GLubyte *str = parseState->pos;
|
|
GLint i = 0, j = 0;
|
|
|
|
token[0] = 0;
|
|
|
|
/* skip whitespace and comments */
|
|
while (str[i] && (IsWhitespace(str[i]) || str[i] == '#')) {
|
|
if (str[i] == '#') {
|
|
/* skip comment */
|
|
while (str[i] && (str[i] != '\n' && str[i] != '\r')) {
|
|
i++;
|
|
}
|
|
if (str[i] == '\n' || str[i] == '\r')
|
|
parseState->curLine = str + i + 1;
|
|
}
|
|
else {
|
|
/* skip whitespace */
|
|
if (str[i] == '\n' || str[i] == '\r')
|
|
parseState->curLine = str + i + 1;
|
|
i++;
|
|
}
|
|
}
|
|
|
|
if (str[i] == 0)
|
|
return -i;
|
|
|
|
/* try matching an integer */
|
|
while (str[i] && IsDigit(str[i])) {
|
|
token[j++] = str[i++];
|
|
}
|
|
if (j > 0 || !str[i]) {
|
|
token[j] = 0;
|
|
return i;
|
|
}
|
|
|
|
/* try matching an identifier */
|
|
if (IsLetter(str[i])) {
|
|
while (str[i] && (IsLetter(str[i]) || IsDigit(str[i]))) {
|
|
token[j++] = str[i++];
|
|
}
|
|
token[j] = 0;
|
|
return i;
|
|
}
|
|
|
|
/* punctuation character */
|
|
if (str[i]) {
|
|
token[0] = str[i++];
|
|
token[1] = 0;
|
|
return i;
|
|
}
|
|
|
|
/* end of input */
|
|
token[0] = 0;
|
|
return i;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get next token from input stream and increment stream pointer past token.
|
|
*/
|
|
static GLboolean
|
|
Parse_Token(struct parse_state *parseState, GLubyte *token)
|
|
{
|
|
GLint i;
|
|
i = GetToken(parseState, token);
|
|
if (i <= 0) {
|
|
parseState->pos += (-i);
|
|
return GL_FALSE;
|
|
}
|
|
parseState->pos += i;
|
|
return GL_TRUE;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get next token from input stream but don't increment stream pointer.
|
|
*/
|
|
static GLboolean
|
|
Peek_Token(struct parse_state *parseState, GLubyte *token)
|
|
{
|
|
GLint i, len;
|
|
i = GetToken(parseState, token);
|
|
if (i <= 0) {
|
|
parseState->pos += (-i);
|
|
return GL_FALSE;
|
|
}
|
|
len = (GLint)_mesa_strlen((const char *) token);
|
|
parseState->pos += (i - len);
|
|
return GL_TRUE;
|
|
}
|
|
|
|
|
|
/**********************************************************************/
|
|
|
|
static const char *InputRegisters[MAX_NV_FRAGMENT_PROGRAM_INPUTS + 1] = {
|
|
"WPOS", "COL0", "COL1", "FOGC",
|
|
"TEX0", "TEX1", "TEX2", "TEX3", "TEX4", "TEX5", "TEX6", "TEX7", NULL
|
|
};
|
|
|
|
static const char *OutputRegisters[MAX_NV_FRAGMENT_PROGRAM_OUTPUTS + 1] = {
|
|
"COLR", "COLH",
|
|
/* These are only allows for register combiners */
|
|
/*
|
|
"TEX0", "TEX1", "TEX2", "TEX3",
|
|
*/
|
|
"DEPR", NULL
|
|
};
|
|
|
|
|
|
|
|
|
|
/**********************************************************************/
|
|
|
|
/**
|
|
* Try to match 'pattern' as the next token after any whitespace/comments.
|
|
*/
|
|
static GLboolean
|
|
Parse_String(struct parse_state *parseState, const char *pattern)
|
|
{
|
|
const GLubyte *m;
|
|
GLint i;
|
|
|
|
/* skip whitespace and comments */
|
|
while (IsWhitespace(*parseState->pos) || *parseState->pos == '#') {
|
|
if (*parseState->pos == '#') {
|
|
while (*parseState->pos && (*parseState->pos != '\n' && *parseState->pos != '\r')) {
|
|
parseState->pos += 1;
|
|
}
|
|
if (*parseState->pos == '\n' || *parseState->pos == '\r')
|
|
parseState->curLine = parseState->pos + 1;
|
|
}
|
|
else {
|
|
/* skip whitespace */
|
|
if (*parseState->pos == '\n' || *parseState->pos == '\r')
|
|
parseState->curLine = parseState->pos + 1;
|
|
parseState->pos += 1;
|
|
}
|
|
}
|
|
|
|
/* Try to match the pattern */
|
|
m = parseState->pos;
|
|
for (i = 0; pattern[i]; i++) {
|
|
if (*m != (GLubyte) pattern[i])
|
|
return GL_FALSE;
|
|
m += 1;
|
|
}
|
|
parseState->pos = m;
|
|
|
|
return GL_TRUE; /* success */
|
|
}
|
|
|
|
|
|
static GLboolean
|
|
Parse_Identifier(struct parse_state *parseState, GLubyte *ident)
|
|
{
|
|
if (!Parse_Token(parseState, ident))
|
|
RETURN_ERROR;
|
|
if (IsLetter(ident[0]))
|
|
return GL_TRUE;
|
|
else
|
|
RETURN_ERROR1("Expected an identfier");
|
|
}
|
|
|
|
|
|
/**
|
|
* Parse a floating point constant, or a defined symbol name.
|
|
* [+/-]N[.N[eN]]
|
|
* Output: number[0 .. 3] will get the value.
|
|
*/
|
|
static GLboolean
|
|
Parse_ScalarConstant(struct parse_state *parseState, GLfloat *number)
|
|
{
|
|
char *end = NULL;
|
|
|
|
*number = (GLfloat) _mesa_strtod((const char *) parseState->pos, &end);
|
|
|
|
if (end && end > (char *) parseState->pos) {
|
|
/* got a number */
|
|
parseState->pos = (GLubyte *) end;
|
|
number[1] = *number;
|
|
number[2] = *number;
|
|
number[3] = *number;
|
|
return GL_TRUE;
|
|
}
|
|
else {
|
|
/* should be an identifier */
|
|
GLubyte ident[100];
|
|
const GLfloat *constant;
|
|
if (!Parse_Identifier(parseState, ident))
|
|
RETURN_ERROR1("Expected an identifier");
|
|
constant = _mesa_lookup_parameter_value(parseState->parameters,
|
|
-1, (const char *) ident);
|
|
/* XXX Check that it's a constant and not a parameter */
|
|
if (!constant) {
|
|
RETURN_ERROR1("Undefined symbol");
|
|
}
|
|
else {
|
|
COPY_4V(number, constant);
|
|
return GL_TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Parse a vector constant, one of:
|
|
* { float }
|
|
* { float, float }
|
|
* { float, float, float }
|
|
* { float, float, float, float }
|
|
*/
|
|
static GLboolean
|
|
Parse_VectorConstant(struct parse_state *parseState, GLfloat *vec)
|
|
{
|
|
/* "{" was already consumed */
|
|
|
|
ASSIGN_4V(vec, 0.0, 0.0, 0.0, 1.0);
|
|
|
|
if (!Parse_ScalarConstant(parseState, vec+0)) /* X */
|
|
return GL_FALSE;
|
|
|
|
if (Parse_String(parseState, "}")) {
|
|
return GL_TRUE;
|
|
}
|
|
|
|
if (!Parse_String(parseState, ","))
|
|
RETURN_ERROR1("Expected comma in vector constant");
|
|
|
|
if (!Parse_ScalarConstant(parseState, vec+1)) /* Y */
|
|
return GL_FALSE;
|
|
|
|
if (Parse_String(parseState, "}")) {
|
|
return GL_TRUE;
|
|
}
|
|
|
|
if (!Parse_String(parseState, ","))
|
|
RETURN_ERROR1("Expected comma in vector constant");
|
|
|
|
if (!Parse_ScalarConstant(parseState, vec+2)) /* Z */
|
|
return GL_FALSE;
|
|
|
|
if (Parse_String(parseState, "}")) {
|
|
return GL_TRUE;
|
|
}
|
|
|
|
if (!Parse_String(parseState, ","))
|
|
RETURN_ERROR1("Expected comma in vector constant");
|
|
|
|
if (!Parse_ScalarConstant(parseState, vec+3)) /* W */
|
|
return GL_FALSE;
|
|
|
|
if (!Parse_String(parseState, "}"))
|
|
RETURN_ERROR1("Expected closing brace in vector constant");
|
|
|
|
return GL_TRUE;
|
|
}
|
|
|
|
|
|
/**
|
|
* Parse <number>, <varname> or {a, b, c, d}.
|
|
* Return number of values in the vector or scalar, or zero if parse error.
|
|
*/
|
|
static GLuint
|
|
Parse_VectorOrScalarConstant(struct parse_state *parseState, GLfloat *vec)
|
|
{
|
|
if (Parse_String(parseState, "{")) {
|
|
return Parse_VectorConstant(parseState, vec);
|
|
}
|
|
else {
|
|
GLboolean b = Parse_ScalarConstant(parseState, vec);
|
|
if (b) {
|
|
vec[1] = vec[2] = vec[3] = vec[0];
|
|
}
|
|
return b;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Parse a texture image source:
|
|
* [TEX0 | TEX1 | .. | TEX15] , [1D | 2D | 3D | CUBE | RECT]
|
|
*/
|
|
static GLboolean
|
|
Parse_TextureImageId(struct parse_state *parseState,
|
|
GLubyte *texUnit, GLubyte *texTargetBit)
|
|
{
|
|
GLubyte imageSrc[100];
|
|
GLint unit;
|
|
|
|
if (!Parse_Token(parseState, imageSrc))
|
|
RETURN_ERROR;
|
|
|
|
if (imageSrc[0] != 'T' ||
|
|
imageSrc[1] != 'E' ||
|
|
imageSrc[2] != 'X') {
|
|
RETURN_ERROR1("Expected TEX# source");
|
|
}
|
|
unit = _mesa_atoi((const char *) imageSrc + 3);
|
|
if ((unit < 0 || unit > MAX_TEXTURE_IMAGE_UNITS) ||
|
|
(unit == 0 && (imageSrc[3] != '0' || imageSrc[4] != 0))) {
|
|
RETURN_ERROR1("Invalied TEX# source index");
|
|
}
|
|
*texUnit = unit;
|
|
|
|
if (!Parse_String(parseState, ","))
|
|
RETURN_ERROR1("Expected ,");
|
|
|
|
if (Parse_String(parseState, "1D")) {
|
|
*texTargetBit = TEXTURE_1D_BIT;
|
|
}
|
|
else if (Parse_String(parseState, "2D")) {
|
|
*texTargetBit = TEXTURE_2D_BIT;
|
|
}
|
|
else if (Parse_String(parseState, "3D")) {
|
|
*texTargetBit = TEXTURE_3D_BIT;
|
|
}
|
|
else if (Parse_String(parseState, "CUBE")) {
|
|
*texTargetBit = TEXTURE_CUBE_BIT;
|
|
}
|
|
else if (Parse_String(parseState, "RECT")) {
|
|
*texTargetBit = TEXTURE_RECT_BIT;
|
|
}
|
|
else {
|
|
RETURN_ERROR1("Invalid texture target token");
|
|
}
|
|
|
|
/* update record of referenced texture units */
|
|
parseState->texturesUsed[*texUnit] |= *texTargetBit;
|
|
if (_mesa_bitcount(parseState->texturesUsed[*texUnit]) > 1) {
|
|
RETURN_ERROR1("Only one texture target can be used per texture unit.");
|
|
}
|
|
|
|
return GL_TRUE;
|
|
}
|
|
|
|
|
|
/**
|
|
* Parse a scalar suffix like .x, .y, .z or .w or parse a swizzle suffix
|
|
* like .wxyz, .xxyy, etc and return the swizzle indexes.
|
|
*/
|
|
static GLboolean
|
|
Parse_SwizzleSuffix(const GLubyte *token, GLuint swizzle[4])
|
|
{
|
|
if (token[1] == 0) {
|
|
/* single letter swizzle (scalar) */
|
|
if (token[0] == 'x')
|
|
ASSIGN_4V(swizzle, 0, 0, 0, 0);
|
|
else if (token[0] == 'y')
|
|
ASSIGN_4V(swizzle, 1, 1, 1, 1);
|
|
else if (token[0] == 'z')
|
|
ASSIGN_4V(swizzle, 2, 2, 2, 2);
|
|
else if (token[0] == 'w')
|
|
ASSIGN_4V(swizzle, 3, 3, 3, 3);
|
|
else
|
|
return GL_FALSE;
|
|
}
|
|
else {
|
|
/* 4-component swizzle (vector) */
|
|
GLint k;
|
|
for (k = 0; token[k] && k < 4; k++) {
|
|
if (token[k] == 'x')
|
|
swizzle[k] = 0;
|
|
else if (token[k] == 'y')
|
|
swizzle[k] = 1;
|
|
else if (token[k] == 'z')
|
|
swizzle[k] = 2;
|
|
else if (token[k] == 'w')
|
|
swizzle[k] = 3;
|
|
else
|
|
return GL_FALSE;
|
|
}
|
|
if (k != 4)
|
|
return GL_FALSE;
|
|
}
|
|
return GL_TRUE;
|
|
}
|
|
|
|
|
|
static GLboolean
|
|
Parse_CondCodeMask(struct parse_state *parseState,
|
|
struct prog_dst_register *dstReg)
|
|
{
|
|
if (Parse_String(parseState, "EQ"))
|
|
dstReg->CondMask = COND_EQ;
|
|
else if (Parse_String(parseState, "GE"))
|
|
dstReg->CondMask = COND_GE;
|
|
else if (Parse_String(parseState, "GT"))
|
|
dstReg->CondMask = COND_GT;
|
|
else if (Parse_String(parseState, "LE"))
|
|
dstReg->CondMask = COND_LE;
|
|
else if (Parse_String(parseState, "LT"))
|
|
dstReg->CondMask = COND_LT;
|
|
else if (Parse_String(parseState, "NE"))
|
|
dstReg->CondMask = COND_NE;
|
|
else if (Parse_String(parseState, "TR"))
|
|
dstReg->CondMask = COND_TR;
|
|
else if (Parse_String(parseState, "FL"))
|
|
dstReg->CondMask = COND_FL;
|
|
else
|
|
RETURN_ERROR1("Invalid condition code mask");
|
|
|
|
/* look for optional .xyzw swizzle */
|
|
if (Parse_String(parseState, ".")) {
|
|
GLubyte token[100];
|
|
GLuint swz[4];
|
|
|
|
if (!Parse_Token(parseState, token)) /* get xyzw suffix */
|
|
RETURN_ERROR;
|
|
|
|
if (!Parse_SwizzleSuffix(token, swz))
|
|
RETURN_ERROR1("Invalid swizzle suffix");
|
|
|
|
dstReg->CondSwizzle = MAKE_SWIZZLE4(swz[0], swz[1], swz[2], swz[3]);
|
|
}
|
|
|
|
return GL_TRUE;
|
|
}
|
|
|
|
|
|
/**
|
|
* Parse a temporary register: Rnn or Hnn
|
|
*/
|
|
static GLboolean
|
|
Parse_TempReg(struct parse_state *parseState, GLint *tempRegNum)
|
|
{
|
|
GLubyte token[100];
|
|
|
|
/* Should be 'R##' or 'H##' */
|
|
if (!Parse_Token(parseState, token))
|
|
RETURN_ERROR;
|
|
if (token[0] != 'R' && token[0] != 'H')
|
|
RETURN_ERROR1("Expected R## or H##");
|
|
|
|
if (IsDigit(token[1])) {
|
|
GLint reg = _mesa_atoi((const char *) (token + 1));
|
|
if (token[0] == 'H')
|
|
reg += 32;
|
|
if (reg >= MAX_NV_FRAGMENT_PROGRAM_TEMPS)
|
|
RETURN_ERROR1("Invalid temporary register name");
|
|
*tempRegNum = reg;
|
|
}
|
|
else {
|
|
RETURN_ERROR1("Invalid temporary register name");
|
|
}
|
|
|
|
return GL_TRUE;
|
|
}
|
|
|
|
|
|
/**
|
|
* Parse a write-only dummy register: RC or HC.
|
|
*/
|
|
static GLboolean
|
|
Parse_DummyReg(struct parse_state *parseState, GLint *regNum)
|
|
{
|
|
if (Parse_String(parseState, "RC")) {
|
|
*regNum = 0;
|
|
}
|
|
else if (Parse_String(parseState, "HC")) {
|
|
*regNum = 1;
|
|
}
|
|
else {
|
|
RETURN_ERROR1("Invalid write-only register name");
|
|
}
|
|
|
|
return GL_TRUE;
|
|
}
|
|
|
|
|
|
/**
|
|
* Parse a program local parameter register "p[##]"
|
|
*/
|
|
static GLboolean
|
|
Parse_ProgramParamReg(struct parse_state *parseState, GLint *regNum)
|
|
{
|
|
GLubyte token[100];
|
|
|
|
if (!Parse_String(parseState, "p["))
|
|
RETURN_ERROR1("Expected p[");
|
|
|
|
if (!Parse_Token(parseState, token))
|
|
RETURN_ERROR;
|
|
|
|
if (IsDigit(token[0])) {
|
|
/* a numbered program parameter register */
|
|
GLint reg = _mesa_atoi((const char *) token);
|
|
if (reg >= MAX_NV_FRAGMENT_PROGRAM_PARAMS)
|
|
RETURN_ERROR1("Invalid constant program number");
|
|
*regNum = reg;
|
|
}
|
|
else {
|
|
RETURN_ERROR;
|
|
}
|
|
|
|
if (!Parse_String(parseState, "]"))
|
|
RETURN_ERROR1("Expected ]");
|
|
|
|
return GL_TRUE;
|
|
}
|
|
|
|
|
|
/**
|
|
* Parse f[name] - fragment input register
|
|
*/
|
|
static GLboolean
|
|
Parse_FragReg(struct parse_state *parseState, GLint *tempRegNum)
|
|
{
|
|
GLubyte token[100];
|
|
GLint j;
|
|
|
|
/* Match 'f[' */
|
|
if (!Parse_String(parseState, "f["))
|
|
RETURN_ERROR1("Expected f[");
|
|
|
|
/* get <name> and look for match */
|
|
if (!Parse_Token(parseState, token)) {
|
|
RETURN_ERROR;
|
|
}
|
|
for (j = 0; InputRegisters[j]; j++) {
|
|
if (_mesa_strcmp((const char *) token, InputRegisters[j]) == 0) {
|
|
*tempRegNum = j;
|
|
parseState->inputsRead |= (1 << j);
|
|
break;
|
|
}
|
|
}
|
|
if (!InputRegisters[j]) {
|
|
/* unknown input register label */
|
|
RETURN_ERROR2("Invalid register name", token);
|
|
}
|
|
|
|
/* Match '[' */
|
|
if (!Parse_String(parseState, "]"))
|
|
RETURN_ERROR1("Expected ]");
|
|
|
|
return GL_TRUE;
|
|
}
|
|
|
|
|
|
static GLboolean
|
|
Parse_OutputReg(struct parse_state *parseState, GLint *outputRegNum)
|
|
{
|
|
GLubyte token[100];
|
|
GLint j;
|
|
|
|
/* Match "o[" */
|
|
if (!Parse_String(parseState, "o["))
|
|
RETURN_ERROR1("Expected o[");
|
|
|
|
/* Get output reg name */
|
|
if (!Parse_Token(parseState, token))
|
|
RETURN_ERROR;
|
|
|
|
/* try to match an output register name */
|
|
for (j = 0; OutputRegisters[j]; j++) {
|
|
if (_mesa_strcmp((const char *) token, OutputRegisters[j]) == 0) {
|
|
static GLuint bothColors = (1 << FRAG_RESULT_COLR) | (1 << FRAG_RESULT_COLH);
|
|
*outputRegNum = j;
|
|
parseState->outputsWritten |= (1 << j);
|
|
if ((parseState->outputsWritten & bothColors) == bothColors) {
|
|
RETURN_ERROR1("Illegal to write to both o[COLR] and o[COLH]");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (!OutputRegisters[j])
|
|
RETURN_ERROR1("Invalid output register name");
|
|
|
|
/* Match ']' */
|
|
if (!Parse_String(parseState, "]"))
|
|
RETURN_ERROR1("Expected ]");
|
|
|
|
return GL_TRUE;
|
|
}
|
|
|
|
|
|
static GLboolean
|
|
Parse_MaskedDstReg(struct parse_state *parseState,
|
|
struct prog_dst_register *dstReg)
|
|
{
|
|
GLubyte token[100];
|
|
GLint idx;
|
|
|
|
/* Dst reg can be R<n>, H<n>, o[n], RC or HC */
|
|
if (!Peek_Token(parseState, token))
|
|
RETURN_ERROR;
|
|
|
|
if (_mesa_strcmp((const char *) token, "RC") == 0 ||
|
|
_mesa_strcmp((const char *) token, "HC") == 0) {
|
|
/* a write-only register */
|
|
dstReg->File = PROGRAM_WRITE_ONLY;
|
|
if (!Parse_DummyReg(parseState, &idx))
|
|
RETURN_ERROR;
|
|
dstReg->Index = idx;
|
|
}
|
|
else if (token[0] == 'R' || token[0] == 'H') {
|
|
/* a temporary register */
|
|
dstReg->File = PROGRAM_TEMPORARY;
|
|
if (!Parse_TempReg(parseState, &idx))
|
|
RETURN_ERROR;
|
|
dstReg->Index = idx;
|
|
}
|
|
else if (token[0] == 'o') {
|
|
/* an output register */
|
|
dstReg->File = PROGRAM_OUTPUT;
|
|
if (!Parse_OutputReg(parseState, &idx))
|
|
RETURN_ERROR;
|
|
dstReg->Index = idx;
|
|
}
|
|
else {
|
|
RETURN_ERROR1("Invalid destination register name");
|
|
}
|
|
|
|
/* Parse optional write mask */
|
|
if (Parse_String(parseState, ".")) {
|
|
/* got a mask */
|
|
GLint k = 0;
|
|
|
|
if (!Parse_Token(parseState, token)) /* get xyzw writemask */
|
|
RETURN_ERROR;
|
|
|
|
dstReg->WriteMask = 0;
|
|
|
|
if (token[k] == 'x') {
|
|
dstReg->WriteMask |= WRITEMASK_X;
|
|
k++;
|
|
}
|
|
if (token[k] == 'y') {
|
|
dstReg->WriteMask |= WRITEMASK_Y;
|
|
k++;
|
|
}
|
|
if (token[k] == 'z') {
|
|
dstReg->WriteMask |= WRITEMASK_Z;
|
|
k++;
|
|
}
|
|
if (token[k] == 'w') {
|
|
dstReg->WriteMask |= WRITEMASK_W;
|
|
k++;
|
|
}
|
|
if (k == 0) {
|
|
RETURN_ERROR1("Invalid writemask character");
|
|
}
|
|
|
|
}
|
|
else {
|
|
dstReg->WriteMask = WRITEMASK_XYZW;
|
|
}
|
|
|
|
/* optional condition code mask */
|
|
if (Parse_String(parseState, "(")) {
|
|
/* ("EQ" | "GE" | "GT" | "LE" | "LT" | "NE" | "TR" | "FL".x|y|z|w) */
|
|
/* ("EQ" | "GE" | "GT" | "LE" | "LT" | "NE" | "TR" | "FL".[xyzw]) */
|
|
if (!Parse_CondCodeMask(parseState, dstReg))
|
|
RETURN_ERROR;
|
|
|
|
if (!Parse_String(parseState, ")")) /* consume ")" */
|
|
RETURN_ERROR1("Expected )");
|
|
|
|
return GL_TRUE;
|
|
}
|
|
else {
|
|
/* no cond code mask */
|
|
dstReg->CondMask = COND_TR;
|
|
dstReg->CondSwizzle = SWIZZLE_NOOP;
|
|
return GL_TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Parse a vector source (register, constant, etc):
|
|
* <vectorSrc> ::= <absVectorSrc>
|
|
* | <baseVectorSrc>
|
|
* <absVectorSrc> ::= <negate> "|" <baseVectorSrc> "|"
|
|
*/
|
|
static GLboolean
|
|
Parse_VectorSrc(struct parse_state *parseState,
|
|
struct prog_src_register *srcReg)
|
|
{
|
|
GLfloat sign = 1.0F;
|
|
GLubyte token[100];
|
|
GLint idx;
|
|
|
|
/*
|
|
* First, take care of +/- and absolute value stuff.
|
|
*/
|
|
if (Parse_String(parseState, "-"))
|
|
sign = -1.0F;
|
|
else if (Parse_String(parseState, "+"))
|
|
sign = +1.0F;
|
|
|
|
if (Parse_String(parseState, "|")) {
|
|
srcReg->Abs = GL_TRUE;
|
|
srcReg->NegateAbs = (sign < 0.0F) ? GL_TRUE : GL_FALSE;
|
|
|
|
if (Parse_String(parseState, "-"))
|
|
srcReg->NegateBase = NEGATE_XYZW;
|
|
else if (Parse_String(parseState, "+"))
|
|
srcReg->NegateBase = NEGATE_NONE;
|
|
else
|
|
srcReg->NegateBase = NEGATE_NONE;
|
|
}
|
|
else {
|
|
srcReg->Abs = GL_FALSE;
|
|
srcReg->NegateAbs = GL_FALSE;
|
|
srcReg->NegateBase = (sign < 0.0F) ? NEGATE_XYZW : NEGATE_NONE;
|
|
}
|
|
|
|
/* This should be the real src vector/register name */
|
|
if (!Peek_Token(parseState, token))
|
|
RETURN_ERROR;
|
|
|
|
/* Src reg can be Rn, Hn, f[n], p[n], a named parameter, a scalar
|
|
* literal or vector literal.
|
|
*/
|
|
if (token[0] == 'R' || token[0] == 'H') {
|
|
srcReg->File = PROGRAM_TEMPORARY;
|
|
if (!Parse_TempReg(parseState, &idx))
|
|
RETURN_ERROR;
|
|
srcReg->Index = idx;
|
|
}
|
|
else if (token[0] == 'f') {
|
|
/* XXX this might be an identifier! */
|
|
srcReg->File = PROGRAM_INPUT;
|
|
if (!Parse_FragReg(parseState, &idx))
|
|
RETURN_ERROR;
|
|
srcReg->Index = idx;
|
|
}
|
|
else if (token[0] == 'p') {
|
|
/* XXX this might be an identifier! */
|
|
srcReg->File = PROGRAM_LOCAL_PARAM;
|
|
if (!Parse_ProgramParamReg(parseState, &idx))
|
|
RETURN_ERROR;
|
|
srcReg->Index = idx;
|
|
}
|
|
else if (IsLetter(token[0])){
|
|
GLubyte ident[100];
|
|
GLint paramIndex;
|
|
if (!Parse_Identifier(parseState, ident))
|
|
RETURN_ERROR;
|
|
paramIndex = _mesa_lookup_parameter_index(parseState->parameters,
|
|
-1, (const char *) ident);
|
|
if (paramIndex < 0) {
|
|
RETURN_ERROR2("Undefined constant or parameter: ", ident);
|
|
}
|
|
srcReg->File = PROGRAM_NAMED_PARAM;
|
|
srcReg->Index = paramIndex;
|
|
}
|
|
else if (IsDigit(token[0]) || token[0] == '-' || token[0] == '+' || token[0] == '.'){
|
|
/* literal scalar constant */
|
|
GLfloat values[4];
|
|
GLuint paramIndex;
|
|
if (!Parse_ScalarConstant(parseState, values))
|
|
RETURN_ERROR;
|
|
paramIndex = _mesa_add_unnamed_constant(parseState->parameters, values);
|
|
srcReg->File = PROGRAM_NAMED_PARAM;
|
|
srcReg->Index = paramIndex;
|
|
}
|
|
else if (token[0] == '{'){
|
|
/* literal vector constant */
|
|
GLfloat values[4];
|
|
GLuint paramIndex;
|
|
(void) Parse_String(parseState, "{");
|
|
if (!Parse_VectorConstant(parseState, values))
|
|
RETURN_ERROR;
|
|
paramIndex = _mesa_add_unnamed_constant(parseState->parameters, values);
|
|
srcReg->File = PROGRAM_NAMED_PARAM;
|
|
srcReg->Index = paramIndex;
|
|
}
|
|
else {
|
|
RETURN_ERROR2("Invalid source register name", token);
|
|
}
|
|
|
|
/* init swizzle fields */
|
|
srcReg->Swizzle = SWIZZLE_NOOP;
|
|
|
|
/* Look for optional swizzle suffix */
|
|
if (Parse_String(parseState, ".")) {
|
|
GLuint swz[4];
|
|
|
|
if (!Parse_Token(parseState, token))
|
|
RETURN_ERROR;
|
|
|
|
if (!Parse_SwizzleSuffix(token, swz))
|
|
RETURN_ERROR1("Invalid swizzle suffix");
|
|
|
|
srcReg->Swizzle = MAKE_SWIZZLE4(swz[0], swz[1], swz[2], swz[3]);
|
|
}
|
|
|
|
/* Finish absolute value */
|
|
if (srcReg->Abs && !Parse_String(parseState, "|")) {
|
|
RETURN_ERROR1("Expected |");
|
|
}
|
|
|
|
return GL_TRUE;
|
|
}
|
|
|
|
|
|
static GLboolean
|
|
Parse_ScalarSrcReg(struct parse_state *parseState,
|
|
struct prog_src_register *srcReg)
|
|
{
|
|
GLubyte token[100];
|
|
GLfloat sign = 1.0F;
|
|
GLboolean needSuffix = GL_TRUE;
|
|
GLint idx;
|
|
|
|
/*
|
|
* First, take care of +/- and absolute value stuff.
|
|
*/
|
|
if (Parse_String(parseState, "-"))
|
|
sign = -1.0F;
|
|
else if (Parse_String(parseState, "+"))
|
|
sign = +1.0F;
|
|
|
|
if (Parse_String(parseState, "|")) {
|
|
srcReg->Abs = GL_TRUE;
|
|
srcReg->NegateAbs = (sign < 0.0F) ? GL_TRUE : GL_FALSE;
|
|
|
|
if (Parse_String(parseState, "-"))
|
|
srcReg->NegateBase = NEGATE_XYZW;
|
|
else if (Parse_String(parseState, "+"))
|
|
srcReg->NegateBase = NEGATE_NONE;
|
|
else
|
|
srcReg->NegateBase = NEGATE_NONE;
|
|
}
|
|
else {
|
|
srcReg->Abs = GL_FALSE;
|
|
srcReg->NegateAbs = GL_FALSE;
|
|
srcReg->NegateBase = (sign < 0.0F) ? NEGATE_XYZW : NEGATE_NONE;
|
|
}
|
|
|
|
if (!Peek_Token(parseState, token))
|
|
RETURN_ERROR;
|
|
|
|
/* Src reg can be R<n>, H<n> or a named fragment attrib */
|
|
if (token[0] == 'R' || token[0] == 'H') {
|
|
srcReg->File = PROGRAM_TEMPORARY;
|
|
if (!Parse_TempReg(parseState, &idx))
|
|
RETURN_ERROR;
|
|
srcReg->Index = idx;
|
|
}
|
|
else if (token[0] == 'f') {
|
|
srcReg->File = PROGRAM_INPUT;
|
|
if (!Parse_FragReg(parseState, &idx))
|
|
RETURN_ERROR;
|
|
srcReg->Index = idx;
|
|
}
|
|
else if (token[0] == '{') {
|
|
/* vector literal */
|
|
GLfloat values[4];
|
|
GLuint paramIndex;
|
|
(void) Parse_String(parseState, "{");
|
|
if (!Parse_VectorConstant(parseState, values))
|
|
RETURN_ERROR;
|
|
paramIndex = _mesa_add_unnamed_constant(parseState->parameters, values);
|
|
srcReg->File = PROGRAM_NAMED_PARAM;
|
|
srcReg->Index = paramIndex;
|
|
}
|
|
else if (IsLetter(token[0])){
|
|
/* named param/constant */
|
|
GLubyte ident[100];
|
|
GLint paramIndex;
|
|
if (!Parse_Identifier(parseState, ident))
|
|
RETURN_ERROR;
|
|
paramIndex = _mesa_lookup_parameter_index(parseState->parameters,
|
|
-1, (const char *) ident);
|
|
if (paramIndex < 0) {
|
|
RETURN_ERROR2("Undefined constant or parameter: ", ident);
|
|
}
|
|
srcReg->File = PROGRAM_NAMED_PARAM;
|
|
srcReg->Index = paramIndex;
|
|
}
|
|
else if (IsDigit(token[0])) {
|
|
/* scalar literal */
|
|
GLfloat values[4];
|
|
GLuint paramIndex;
|
|
if (!Parse_ScalarConstant(parseState, values))
|
|
RETURN_ERROR;
|
|
paramIndex = _mesa_add_unnamed_constant(parseState->parameters, values);
|
|
srcReg->Index = paramIndex;
|
|
srcReg->File = PROGRAM_NAMED_PARAM;
|
|
needSuffix = GL_FALSE;
|
|
}
|
|
else {
|
|
RETURN_ERROR2("Invalid scalar source argument", token);
|
|
}
|
|
|
|
srcReg->Swizzle = 0;
|
|
if (needSuffix) {
|
|
/* parse .[xyzw] suffix */
|
|
if (!Parse_String(parseState, "."))
|
|
RETURN_ERROR1("Expected .");
|
|
|
|
if (!Parse_Token(parseState, token))
|
|
RETURN_ERROR;
|
|
|
|
if (token[0] == 'x' && token[1] == 0) {
|
|
srcReg->Swizzle = 0;
|
|
}
|
|
else if (token[0] == 'y' && token[1] == 0) {
|
|
srcReg->Swizzle = 1;
|
|
}
|
|
else if (token[0] == 'z' && token[1] == 0) {
|
|
srcReg->Swizzle = 2;
|
|
}
|
|
else if (token[0] == 'w' && token[1] == 0) {
|
|
srcReg->Swizzle = 3;
|
|
}
|
|
else {
|
|
RETURN_ERROR1("Invalid scalar source suffix");
|
|
}
|
|
}
|
|
|
|
/* Finish absolute value */
|
|
if (srcReg->Abs && !Parse_String(parseState, "|")) {
|
|
RETURN_ERROR1("Expected |");
|
|
}
|
|
|
|
return GL_TRUE;
|
|
}
|
|
|
|
|
|
static GLboolean
|
|
Parse_PrintInstruction(struct parse_state *parseState,
|
|
struct prog_instruction *inst)
|
|
{
|
|
const GLubyte *str;
|
|
GLubyte *msg;
|
|
GLuint len;
|
|
GLint idx;
|
|
|
|
/* The first argument is a literal string 'just like this' */
|
|
if (!Parse_String(parseState, "'"))
|
|
RETURN_ERROR1("Expected '");
|
|
|
|
str = parseState->pos;
|
|
for (len = 0; str[len] != '\''; len++) /* find closing quote */
|
|
;
|
|
parseState->pos += len + 1;
|
|
msg = (GLubyte*) _mesa_malloc(len + 1);
|
|
|
|
_mesa_memcpy(msg, str, len);
|
|
msg[len] = 0;
|
|
inst->Data = msg;
|
|
|
|
if (Parse_String(parseState, ",")) {
|
|
/* got an optional register to print */
|
|
GLubyte token[100];
|
|
GetToken(parseState, token);
|
|
if (token[0] == 'o') {
|
|
/* dst reg */
|
|
if (!Parse_OutputReg(parseState, &idx))
|
|
RETURN_ERROR;
|
|
inst->SrcReg[0].Index = idx;
|
|
inst->SrcReg[0].File = PROGRAM_OUTPUT;
|
|
}
|
|
else {
|
|
/* src reg */
|
|
if (!Parse_VectorSrc(parseState, &inst->SrcReg[0]))
|
|
RETURN_ERROR;
|
|
}
|
|
}
|
|
else {
|
|
inst->SrcReg[0].File = PROGRAM_UNDEFINED;
|
|
}
|
|
|
|
inst->SrcReg[0].Swizzle = SWIZZLE_NOOP;
|
|
inst->SrcReg[0].NegateBase = NEGATE_NONE;
|
|
inst->SrcReg[0].Abs = GL_FALSE;
|
|
inst->SrcReg[0].NegateAbs = GL_FALSE;
|
|
|
|
return GL_TRUE;
|
|
}
|
|
|
|
|
|
static GLboolean
|
|
Parse_InstructionSequence(struct parse_state *parseState,
|
|
struct prog_instruction program[])
|
|
{
|
|
while (1) {
|
|
struct prog_instruction *inst = program + parseState->numInst;
|
|
struct instruction_pattern instMatch;
|
|
GLubyte token[100];
|
|
|
|
/* Initialize the instruction */
|
|
_mesa_init_instruction(inst);
|
|
|
|
/* special instructions */
|
|
if (Parse_String(parseState, "DEFINE")) {
|
|
GLubyte id[100];
|
|
GLfloat value[7]; /* yes, 7 to be safe */
|
|
if (!Parse_Identifier(parseState, id))
|
|
RETURN_ERROR;
|
|
/* XXX make sure id is not a reserved identifer, like R9 */
|
|
if (!Parse_String(parseState, "="))
|
|
RETURN_ERROR1("Expected =");
|
|
if (!Parse_VectorOrScalarConstant(parseState, value))
|
|
RETURN_ERROR;
|
|
if (!Parse_String(parseState, ";"))
|
|
RETURN_ERROR1("Expected ;");
|
|
if (_mesa_lookup_parameter_index(parseState->parameters,
|
|
-1, (const char *) id) >= 0) {
|
|
RETURN_ERROR2(id, "already defined");
|
|
}
|
|
_mesa_add_named_parameter(parseState->parameters,
|
|
(const char *) id, value);
|
|
}
|
|
else if (Parse_String(parseState, "DECLARE")) {
|
|
GLubyte id[100];
|
|
GLfloat value[7] = {0, 0, 0, 0, 0, 0, 0}; /* yes, to be safe */
|
|
if (!Parse_Identifier(parseState, id))
|
|
RETURN_ERROR;
|
|
/* XXX make sure id is not a reserved identifer, like R9 */
|
|
if (Parse_String(parseState, "=")) {
|
|
if (!Parse_VectorOrScalarConstant(parseState, value))
|
|
RETURN_ERROR;
|
|
}
|
|
if (!Parse_String(parseState, ";"))
|
|
RETURN_ERROR1("Expected ;");
|
|
if (_mesa_lookup_parameter_index(parseState->parameters,
|
|
-1, (const char *) id) >= 0) {
|
|
RETURN_ERROR2(id, "already declared");
|
|
}
|
|
_mesa_add_named_parameter(parseState->parameters,
|
|
(const char *) id, value);
|
|
}
|
|
else if (Parse_String(parseState, "END")) {
|
|
inst->Opcode = OPCODE_END;
|
|
inst->StringPos = parseState->curLine - parseState->start;
|
|
assert(inst->StringPos >= 0);
|
|
parseState->numInst++;
|
|
if (Parse_Token(parseState, token)) {
|
|
RETURN_ERROR1("Code after END opcode.");
|
|
}
|
|
break;
|
|
}
|
|
else {
|
|
/* general/arithmetic instruction */
|
|
|
|
/* get token */
|
|
if (!Parse_Token(parseState, token)) {
|
|
RETURN_ERROR1("Missing END instruction.");
|
|
}
|
|
|
|
/* try to find matching instuction */
|
|
instMatch = MatchInstruction(token);
|
|
if (instMatch.opcode >= MAX_OPCODE) {
|
|
/* bad instruction name */
|
|
RETURN_ERROR2("Unexpected token: ", token);
|
|
}
|
|
|
|
inst->Opcode = instMatch.opcode;
|
|
inst->Precision = instMatch.suffixes & (_R | _H | _X);
|
|
inst->SaturateMode = (instMatch.suffixes & (_S))
|
|
? SATURATE_ZERO_ONE : SATURATE_OFF;
|
|
inst->CondUpdate = (instMatch.suffixes & (_C)) ? GL_TRUE : GL_FALSE;
|
|
inst->StringPos = parseState->curLine - parseState->start;
|
|
assert(inst->StringPos >= 0);
|
|
|
|
/*
|
|
* parse the input and output operands
|
|
*/
|
|
if (instMatch.outputs == OUTPUT_S || instMatch.outputs == OUTPUT_V) {
|
|
if (!Parse_MaskedDstReg(parseState, &inst->DstReg))
|
|
RETURN_ERROR;
|
|
if (!Parse_String(parseState, ","))
|
|
RETURN_ERROR1("Expected ,");
|
|
}
|
|
else if (instMatch.outputs == OUTPUT_NONE) {
|
|
if (instMatch.opcode == OPCODE_KIL_NV) {
|
|
/* This is a little weird, the cond code info is in
|
|
* the dest register.
|
|
*/
|
|
if (!Parse_CondCodeMask(parseState, &inst->DstReg))
|
|
RETURN_ERROR;
|
|
}
|
|
else {
|
|
ASSERT(instMatch.opcode == OPCODE_PRINT);
|
|
}
|
|
}
|
|
|
|
if (instMatch.inputs == INPUT_1V) {
|
|
if (!Parse_VectorSrc(parseState, &inst->SrcReg[0]))
|
|
RETURN_ERROR;
|
|
}
|
|
else if (instMatch.inputs == INPUT_2V) {
|
|
if (!Parse_VectorSrc(parseState, &inst->SrcReg[0]))
|
|
RETURN_ERROR;
|
|
if (!Parse_String(parseState, ","))
|
|
RETURN_ERROR1("Expected ,");
|
|
if (!Parse_VectorSrc(parseState, &inst->SrcReg[1]))
|
|
RETURN_ERROR;
|
|
}
|
|
else if (instMatch.inputs == INPUT_3V) {
|
|
if (!Parse_VectorSrc(parseState, &inst->SrcReg[0]))
|
|
RETURN_ERROR;
|
|
if (!Parse_String(parseState, ","))
|
|
RETURN_ERROR1("Expected ,");
|
|
if (!Parse_VectorSrc(parseState, &inst->SrcReg[1]))
|
|
RETURN_ERROR;
|
|
if (!Parse_String(parseState, ","))
|
|
RETURN_ERROR1("Expected ,");
|
|
if (!Parse_VectorSrc(parseState, &inst->SrcReg[2]))
|
|
RETURN_ERROR;
|
|
}
|
|
else if (instMatch.inputs == INPUT_1S) {
|
|
if (!Parse_ScalarSrcReg(parseState, &inst->SrcReg[0]))
|
|
RETURN_ERROR;
|
|
}
|
|
else if (instMatch.inputs == INPUT_2S) {
|
|
if (!Parse_ScalarSrcReg(parseState, &inst->SrcReg[0]))
|
|
RETURN_ERROR;
|
|
if (!Parse_String(parseState, ","))
|
|
RETURN_ERROR1("Expected ,");
|
|
if (!Parse_ScalarSrcReg(parseState, &inst->SrcReg[1]))
|
|
RETURN_ERROR;
|
|
}
|
|
else if (instMatch.inputs == INPUT_CC) {
|
|
/* XXX to-do */
|
|
}
|
|
else if (instMatch.inputs == INPUT_1V_T) {
|
|
GLubyte unit, idx;
|
|
if (!Parse_VectorSrc(parseState, &inst->SrcReg[0]))
|
|
RETURN_ERROR;
|
|
if (!Parse_String(parseState, ","))
|
|
RETURN_ERROR1("Expected ,");
|
|
if (!Parse_TextureImageId(parseState, &unit, &idx))
|
|
RETURN_ERROR;
|
|
inst->TexSrcUnit = unit;
|
|
inst->TexSrcTarget = idx;
|
|
}
|
|
else if (instMatch.inputs == INPUT_3V_T) {
|
|
GLubyte unit, idx;
|
|
if (!Parse_VectorSrc(parseState, &inst->SrcReg[0]))
|
|
RETURN_ERROR;
|
|
if (!Parse_String(parseState, ","))
|
|
RETURN_ERROR1("Expected ,");
|
|
if (!Parse_VectorSrc(parseState, &inst->SrcReg[1]))
|
|
RETURN_ERROR;
|
|
if (!Parse_String(parseState, ","))
|
|
RETURN_ERROR1("Expected ,");
|
|
if (!Parse_VectorSrc(parseState, &inst->SrcReg[2]))
|
|
RETURN_ERROR;
|
|
if (!Parse_String(parseState, ","))
|
|
RETURN_ERROR1("Expected ,");
|
|
if (!Parse_TextureImageId(parseState, &unit, &idx))
|
|
RETURN_ERROR;
|
|
inst->TexSrcUnit = unit;
|
|
inst->TexSrcTarget = idx;
|
|
}
|
|
else if (instMatch.inputs == INPUT_1V_S) {
|
|
if (!Parse_PrintInstruction(parseState, inst))
|
|
RETURN_ERROR;
|
|
}
|
|
|
|
/* end of statement semicolon */
|
|
if (!Parse_String(parseState, ";"))
|
|
RETURN_ERROR1("Expected ;");
|
|
|
|
parseState->numInst++;
|
|
|
|
if (parseState->numInst >= MAX_NV_FRAGMENT_PROGRAM_INSTRUCTIONS)
|
|
RETURN_ERROR1("Program too long");
|
|
}
|
|
}
|
|
return GL_TRUE;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Parse/compile the 'str' returning the compiled 'program'.
|
|
* ctx->Program.ErrorPos will be -1 if successful. Otherwise, ErrorPos
|
|
* indicates the position of the error in 'str'.
|
|
*/
|
|
void
|
|
_mesa_parse_nv_fragment_program(GLcontext *ctx, GLenum dstTarget,
|
|
const GLubyte *str, GLsizei len,
|
|
struct gl_fragment_program *program)
|
|
{
|
|
struct parse_state parseState;
|
|
struct prog_instruction instBuffer[MAX_NV_FRAGMENT_PROGRAM_INSTRUCTIONS];
|
|
struct prog_instruction *newInst;
|
|
GLenum target;
|
|
GLubyte *programString;
|
|
|
|
/* Make a null-terminated copy of the program string */
|
|
programString = (GLubyte *) MALLOC(len + 1);
|
|
if (!programString) {
|
|
_mesa_error(ctx, GL_OUT_OF_MEMORY, "glLoadProgramNV");
|
|
return;
|
|
}
|
|
MEMCPY(programString, str, len);
|
|
programString[len] = 0;
|
|
|
|
/* Get ready to parse */
|
|
_mesa_bzero(&parseState, sizeof(struct parse_state));
|
|
parseState.ctx = ctx;
|
|
parseState.start = programString;
|
|
parseState.program = program;
|
|
parseState.numInst = 0;
|
|
parseState.curLine = programString;
|
|
parseState.parameters = _mesa_new_parameter_list();
|
|
|
|
/* Reset error state */
|
|
_mesa_set_program_error(ctx, -1, NULL);
|
|
|
|
/* check the program header */
|
|
if (_mesa_strncmp((const char *) programString, "!!FP1.0", 7) == 0) {
|
|
target = GL_FRAGMENT_PROGRAM_NV;
|
|
parseState.pos = programString + 7;
|
|
}
|
|
else if (_mesa_strncmp((const char *) programString, "!!FCP1.0", 8) == 0) {
|
|
/* fragment / register combiner program - not supported */
|
|
_mesa_set_program_error(ctx, 0, "Invalid fragment program header");
|
|
_mesa_error(ctx, GL_INVALID_OPERATION, "glLoadProgramNV(bad header)");
|
|
return;
|
|
}
|
|
else {
|
|
/* invalid header */
|
|
_mesa_set_program_error(ctx, 0, "Invalid fragment program header");
|
|
_mesa_error(ctx, GL_INVALID_OPERATION, "glLoadProgramNV(bad header)");
|
|
return;
|
|
}
|
|
|
|
/* make sure target and header match */
|
|
if (target != dstTarget) {
|
|
_mesa_error(ctx, GL_INVALID_OPERATION,
|
|
"glLoadProgramNV(target mismatch 0x%x != 0x%x)",
|
|
target, dstTarget);
|
|
return;
|
|
}
|
|
|
|
if (Parse_InstructionSequence(&parseState, instBuffer)) {
|
|
GLuint u;
|
|
/* successful parse! */
|
|
|
|
if (parseState.outputsWritten == 0) {
|
|
/* must write at least one output! */
|
|
_mesa_error(ctx, GL_INVALID_OPERATION,
|
|
"Invalid fragment program - no outputs written.");
|
|
return;
|
|
}
|
|
|
|
/* copy the compiled instructions */
|
|
assert(parseState.numInst <= MAX_NV_FRAGMENT_PROGRAM_INSTRUCTIONS);
|
|
newInst = (struct prog_instruction *)
|
|
MALLOC(parseState.numInst * sizeof(struct prog_instruction));
|
|
if (!newInst) {
|
|
_mesa_error(ctx, GL_OUT_OF_MEMORY, "glLoadProgramNV");
|
|
return; /* out of memory */
|
|
}
|
|
MEMCPY(newInst, instBuffer,
|
|
parseState.numInst * sizeof(struct prog_instruction));
|
|
|
|
/* install the program */
|
|
program->Base.Target = target;
|
|
if (program->Base.String) {
|
|
FREE(program->Base.String);
|
|
}
|
|
program->Base.String = programString;
|
|
program->Base.Format = GL_PROGRAM_FORMAT_ASCII_ARB;
|
|
if (program->Base.Instructions) {
|
|
_mesa_free(program->Base.Instructions);
|
|
}
|
|
program->Base.Instructions = newInst;
|
|
program->Base.NumInstructions = parseState.numInst;
|
|
program->Base.InputsRead = parseState.inputsRead;
|
|
program->Base.OutputsWritten = parseState.outputsWritten;
|
|
for (u = 0; u < ctx->Const.MaxTextureImageUnits; u++)
|
|
program->TexturesUsed[u] = parseState.texturesUsed[u];
|
|
|
|
/* save program parameters */
|
|
program->Base.Parameters = parseState.parameters;
|
|
|
|
/* allocate registers for declared program parameters */
|
|
#if 00
|
|
_mesa_assign_program_registers(&(program->SymbolTable));
|
|
#endif
|
|
|
|
#ifdef DEBUG_foo
|
|
_mesa_printf("--- glLoadProgramNV(%d) result ---\n", program->Base.Id);
|
|
_mesa_print_nv_fragment_program(program);
|
|
_mesa_printf("----------------------------------\n");
|
|
#endif
|
|
}
|
|
else {
|
|
/* Error! */
|
|
_mesa_error(ctx, GL_INVALID_OPERATION, "glLoadProgramNV");
|
|
/* NOTE: _mesa_set_program_error would have been called already */
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
PrintSrcReg(const struct gl_fragment_program *program,
|
|
const struct prog_src_register *src)
|
|
{
|
|
static const char comps[5] = "xyzw";
|
|
|
|
if (src->NegateAbs) {
|
|
_mesa_printf("-");
|
|
}
|
|
if (src->Abs) {
|
|
_mesa_printf("|");
|
|
}
|
|
if (src->NegateBase) {
|
|
_mesa_printf("-");
|
|
}
|
|
if (src->File == PROGRAM_NAMED_PARAM) {
|
|
if (program->Base.Parameters->Parameters[src->Index].Type
|
|
== PROGRAM_CONSTANT) {
|
|
const GLfloat *v;
|
|
v = program->Base.Parameters->ParameterValues[src->Index];
|
|
_mesa_printf("{%g, %g, %g, %g}", v[0], v[1], v[2], v[3]);
|
|
}
|
|
else {
|
|
ASSERT(program->Base.Parameters->Parameters[src->Index].Type
|
|
== PROGRAM_NAMED_PARAM);
|
|
_mesa_printf("%s", program->Base.Parameters->Parameters[src->Index].Name);
|
|
}
|
|
}
|
|
else if (src->File == PROGRAM_OUTPUT) {
|
|
_mesa_printf("o[%s]", OutputRegisters[src->Index]);
|
|
}
|
|
else if (src->File == PROGRAM_INPUT) {
|
|
_mesa_printf("f[%s]", InputRegisters[src->Index]);
|
|
}
|
|
else if (src->File == PROGRAM_LOCAL_PARAM) {
|
|
_mesa_printf("p[%d]", src->Index);
|
|
}
|
|
else if (src->File == PROGRAM_TEMPORARY) {
|
|
if (src->Index >= 32)
|
|
_mesa_printf("H%d", src->Index);
|
|
else
|
|
_mesa_printf("R%d", src->Index);
|
|
}
|
|
else if (src->File == PROGRAM_WRITE_ONLY) {
|
|
_mesa_printf("%cC", "HR"[src->Index]);
|
|
}
|
|
else {
|
|
_mesa_problem(NULL, "Invalid fragment register %d", src->Index);
|
|
return;
|
|
}
|
|
if (GET_SWZ(src->Swizzle, 0) == GET_SWZ(src->Swizzle, 1) &&
|
|
GET_SWZ(src->Swizzle, 0) == GET_SWZ(src->Swizzle, 2) &&
|
|
GET_SWZ(src->Swizzle, 0) == GET_SWZ(src->Swizzle, 3)) {
|
|
_mesa_printf(".%c", comps[GET_SWZ(src->Swizzle, 0)]);
|
|
}
|
|
else if (src->Swizzle != SWIZZLE_NOOP) {
|
|
_mesa_printf(".%c%c%c%c",
|
|
comps[GET_SWZ(src->Swizzle, 0)],
|
|
comps[GET_SWZ(src->Swizzle, 1)],
|
|
comps[GET_SWZ(src->Swizzle, 2)],
|
|
comps[GET_SWZ(src->Swizzle, 3)]);
|
|
}
|
|
if (src->Abs) {
|
|
_mesa_printf("|");
|
|
}
|
|
}
|
|
|
|
static void
|
|
PrintTextureSrc(const struct prog_instruction *inst)
|
|
{
|
|
_mesa_printf("TEX%d, ", inst->TexSrcUnit);
|
|
switch (inst->TexSrcTarget) {
|
|
case TEXTURE_1D_INDEX:
|
|
_mesa_printf("1D");
|
|
break;
|
|
case TEXTURE_2D_INDEX:
|
|
_mesa_printf("2D");
|
|
break;
|
|
case TEXTURE_3D_INDEX:
|
|
_mesa_printf("3D");
|
|
break;
|
|
case TEXTURE_RECT_INDEX:
|
|
_mesa_printf("RECT");
|
|
break;
|
|
case TEXTURE_CUBE_INDEX:
|
|
_mesa_printf("CUBE");
|
|
break;
|
|
default:
|
|
_mesa_problem(NULL, "Invalid textue target in PrintTextureSrc");
|
|
}
|
|
}
|
|
|
|
static void
|
|
PrintCondCode(const struct prog_dst_register *dst)
|
|
{
|
|
static const char *comps = "xyzw";
|
|
static const char *ccString[] = {
|
|
"??", "GT", "EQ", "LT", "UN", "GE", "LE", "NE", "TR", "FL", "??"
|
|
};
|
|
|
|
_mesa_printf("%s", ccString[dst->CondMask]);
|
|
if (GET_SWZ(dst->CondSwizzle, 0) == GET_SWZ(dst->CondSwizzle, 1) &&
|
|
GET_SWZ(dst->CondSwizzle, 0) == GET_SWZ(dst->CondSwizzle, 2) &&
|
|
GET_SWZ(dst->CondSwizzle, 0) == GET_SWZ(dst->CondSwizzle, 3)) {
|
|
_mesa_printf(".%c", comps[GET_SWZ(dst->CondSwizzle, 0)]);
|
|
}
|
|
else if (dst->CondSwizzle != SWIZZLE_NOOP) {
|
|
_mesa_printf(".%c%c%c%c",
|
|
comps[GET_SWZ(dst->CondSwizzle, 0)],
|
|
comps[GET_SWZ(dst->CondSwizzle, 1)],
|
|
comps[GET_SWZ(dst->CondSwizzle, 2)],
|
|
comps[GET_SWZ(dst->CondSwizzle, 3)]);
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
PrintDstReg(const struct prog_dst_register *dst)
|
|
{
|
|
if (dst->File == PROGRAM_OUTPUT) {
|
|
_mesa_printf("o[%s]", OutputRegisters[dst->Index]);
|
|
}
|
|
else if (dst->File == PROGRAM_TEMPORARY) {
|
|
if (dst->Index >= 32)
|
|
_mesa_printf("H%d", dst->Index);
|
|
else
|
|
_mesa_printf("R%d", dst->Index);
|
|
}
|
|
else if (dst->File == PROGRAM_LOCAL_PARAM) {
|
|
_mesa_printf("p[%d]", dst->Index);
|
|
}
|
|
else if (dst->File == PROGRAM_WRITE_ONLY) {
|
|
_mesa_printf("%cC", "HR"[dst->Index]);
|
|
}
|
|
else {
|
|
_mesa_printf("???");
|
|
}
|
|
|
|
if (dst->WriteMask != 0 && dst->WriteMask != WRITEMASK_XYZW) {
|
|
_mesa_printf(".");
|
|
if (dst->WriteMask & WRITEMASK_X)
|
|
_mesa_printf("x");
|
|
if (dst->WriteMask & WRITEMASK_Y)
|
|
_mesa_printf("y");
|
|
if (dst->WriteMask & WRITEMASK_Z)
|
|
_mesa_printf("z");
|
|
if (dst->WriteMask & WRITEMASK_W)
|
|
_mesa_printf("w");
|
|
}
|
|
|
|
if (dst->CondMask != COND_TR ||
|
|
dst->CondSwizzle != SWIZZLE_NOOP) {
|
|
_mesa_printf(" (");
|
|
PrintCondCode(dst);
|
|
_mesa_printf(")");
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Print (unparse) the given vertex program. Just for debugging.
|
|
*/
|
|
void
|
|
_mesa_print_nv_fragment_program(const struct gl_fragment_program *program)
|
|
{
|
|
const struct prog_instruction *inst;
|
|
|
|
for (inst = program->Base.Instructions; inst->Opcode != OPCODE_END; inst++) {
|
|
int i;
|
|
for (i = 0; Instructions[i].name; i++) {
|
|
if (inst->Opcode == Instructions[i].opcode) {
|
|
/* print instruction name */
|
|
_mesa_printf("%s", Instructions[i].name);
|
|
if (inst->Precision == FLOAT16)
|
|
_mesa_printf("H");
|
|
else if (inst->Precision == FIXED12)
|
|
_mesa_printf("X");
|
|
if (inst->CondUpdate)
|
|
_mesa_printf("C");
|
|
if (inst->SaturateMode == SATURATE_ZERO_ONE)
|
|
_mesa_printf("_SAT");
|
|
_mesa_printf(" ");
|
|
|
|
if (Instructions[i].inputs == INPUT_CC) {
|
|
PrintCondCode(&inst->DstReg);
|
|
}
|
|
else if (Instructions[i].outputs == OUTPUT_V ||
|
|
Instructions[i].outputs == OUTPUT_S) {
|
|
/* print dest register */
|
|
PrintDstReg(&inst->DstReg);
|
|
_mesa_printf(", ");
|
|
}
|
|
|
|
/* print source register(s) */
|
|
if (Instructions[i].inputs == INPUT_1V ||
|
|
Instructions[i].inputs == INPUT_1S) {
|
|
PrintSrcReg(program, &inst->SrcReg[0]);
|
|
}
|
|
else if (Instructions[i].inputs == INPUT_2V ||
|
|
Instructions[i].inputs == INPUT_2S) {
|
|
PrintSrcReg(program, &inst->SrcReg[0]);
|
|
_mesa_printf(", ");
|
|
PrintSrcReg(program, &inst->SrcReg[1]);
|
|
}
|
|
else if (Instructions[i].inputs == INPUT_3V) {
|
|
PrintSrcReg(program, &inst->SrcReg[0]);
|
|
_mesa_printf(", ");
|
|
PrintSrcReg(program, &inst->SrcReg[1]);
|
|
_mesa_printf(", ");
|
|
PrintSrcReg(program, &inst->SrcReg[2]);
|
|
}
|
|
else if (Instructions[i].inputs == INPUT_1V_T) {
|
|
PrintSrcReg(program, &inst->SrcReg[0]);
|
|
_mesa_printf(", ");
|
|
PrintTextureSrc(inst);
|
|
}
|
|
else if (Instructions[i].inputs == INPUT_3V_T) {
|
|
PrintSrcReg(program, &inst->SrcReg[0]);
|
|
_mesa_printf(", ");
|
|
PrintSrcReg(program, &inst->SrcReg[1]);
|
|
_mesa_printf(", ");
|
|
PrintSrcReg(program, &inst->SrcReg[2]);
|
|
_mesa_printf(", ");
|
|
PrintTextureSrc(inst);
|
|
}
|
|
_mesa_printf(";\n");
|
|
break;
|
|
}
|
|
}
|
|
if (!Instructions[i].name) {
|
|
_mesa_printf("Invalid opcode %d\n", inst->Opcode);
|
|
}
|
|
}
|
|
_mesa_printf("END\n");
|
|
}
|
|
|
|
|
|
const char *
|
|
_mesa_nv_fragment_input_register_name(GLuint i)
|
|
{
|
|
ASSERT(i < MAX_NV_FRAGMENT_PROGRAM_INPUTS);
|
|
return InputRegisters[i];
|
|
}
|
|
|
|
|
|
const char *
|
|
_mesa_nv_fragment_output_register_name(GLuint i)
|
|
{
|
|
ASSERT(i < MAX_NV_FRAGMENT_PROGRAM_OUTPUTS);
|
|
return OutputRegisters[i];
|
|
}
|