📄 nvfragparse.c
字号:
/*
* Mesa 3-D graphics library
* Version: 6.4
*
* 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 "nvfragprog.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 fp_opcode opcode;
GLuint inputs;
GLuint outputs;
GLuint suffixes;
};
static const struct instruction_pattern Instructions[] = {
{ "ADD", FP_OPCODE_ADD, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
{ "COS", FP_OPCODE_COS, INPUT_1S, OUTPUT_S, _R | _H | _C | _S },
{ "DDX", FP_OPCODE_DDX, INPUT_1V, OUTPUT_V, _R | _H | _C | _S },
{ "DDY", FP_OPCODE_DDY, INPUT_1V, OUTPUT_V, _R | _H | _C | _S },
{ "DP3", FP_OPCODE_DP3, INPUT_2V, OUTPUT_S, _R | _H | _X | _C | _S },
{ "DP4", FP_OPCODE_DP4, INPUT_2V, OUTPUT_S, _R | _H | _X | _C | _S },
{ "DST", FP_OPCODE_DP4, INPUT_2V, OUTPUT_V, _R | _H | _C | _S },
{ "EX2", FP_OPCODE_DP4, INPUT_1S, OUTPUT_S, _R | _H | _C | _S },
{ "FLR", FP_OPCODE_FLR, INPUT_1V, OUTPUT_V, _R | _H | _X | _C | _S },
{ "FRC", FP_OPCODE_FRC, INPUT_1V, OUTPUT_V, _R | _H | _X | _C | _S },
{ "KIL", FP_OPCODE_KIL_NV, INPUT_CC, OUTPUT_NONE, 0 },
{ "LG2", FP_OPCODE_LG2, INPUT_1S, OUTPUT_S, _R | _H | _C | _S },
{ "LIT", FP_OPCODE_LIT, INPUT_1V, OUTPUT_V, _R | _H | _C | _S },
{ "LRP", FP_OPCODE_LRP, INPUT_3V, OUTPUT_V, _R | _H | _X | _C | _S },
{ "MAD", FP_OPCODE_MAD, INPUT_3V, OUTPUT_V, _R | _H | _X | _C | _S },
{ "MAX", FP_OPCODE_MAX, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
{ "MIN", FP_OPCODE_MIN, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
{ "MOV", FP_OPCODE_MOV, INPUT_1V, OUTPUT_V, _R | _H | _X | _C | _S },
{ "MUL", FP_OPCODE_MUL, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
{ "PK2H", FP_OPCODE_PK2H, INPUT_1V, OUTPUT_S, 0 },
{ "PK2US", FP_OPCODE_PK2US, INPUT_1V, OUTPUT_S, 0 },
{ "PK4B", FP_OPCODE_PK4B, INPUT_1V, OUTPUT_S, 0 },
{ "PK4UB", FP_OPCODE_PK4UB, INPUT_1V, OUTPUT_S, 0 },
{ "POW", FP_OPCODE_POW, INPUT_2S, OUTPUT_S, _R | _H | _C | _S },
{ "RCP", FP_OPCODE_RCP, INPUT_1S, OUTPUT_S, _R | _H | _C | _S },
{ "RFL", FP_OPCODE_RFL, INPUT_2V, OUTPUT_V, _R | _H | _C | _S },
{ "RSQ", FP_OPCODE_RSQ, INPUT_1S, OUTPUT_S, _R | _H | _C | _S },
{ "SEQ", FP_OPCODE_SEQ, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
{ "SFL", FP_OPCODE_SFL, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
{ "SGE", FP_OPCODE_SGE, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
{ "SGT", FP_OPCODE_SGT, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
{ "SIN", FP_OPCODE_SIN, INPUT_1S, OUTPUT_S, _R | _H | _C | _S },
{ "SLE", FP_OPCODE_SLE, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
{ "SLT", FP_OPCODE_SLT, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
{ "SNE", FP_OPCODE_SNE, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
{ "STR", FP_OPCODE_STR, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
{ "SUB", FP_OPCODE_SUB, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
{ "TEX", FP_OPCODE_TEX, INPUT_1V_T, OUTPUT_V, _C | _S },
{ "TXD", FP_OPCODE_TXD, INPUT_3V_T, OUTPUT_V, _C | _S },
{ "TXP", FP_OPCODE_TXP_NV, INPUT_1V_T, OUTPUT_V, _C | _S },
{ "UP2H", FP_OPCODE_UP2H, INPUT_1S, OUTPUT_V, _C | _S },
{ "UP2US", FP_OPCODE_UP2US, INPUT_1S, OUTPUT_V, _C | _S },
{ "UP4B", FP_OPCODE_UP4B, INPUT_1S, OUTPUT_V, _C | _S },
{ "UP4UB", FP_OPCODE_UP4UB, INPUT_1S, OUTPUT_V, _C | _S },
{ "X2D", FP_OPCODE_X2D, INPUT_3V, OUTPUT_V, _R | _H | _C | _S },
{ "PRINT", FP_OPCODE_PRINT, INPUT_1V_S, OUTPUT_NONE, 0 },
{ NULL, (enum fp_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 fragment_program *program; /* current program */
struct 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 = (enum fp_opcode) -1;
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.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -