1455 lines
39 KiB
C
1455 lines
39 KiB
C
/*
|
|
* Mesa 3-D graphics library
|
|
* Version: 6.5.2
|
|
*
|
|
* Copyright (C) 1999-2006 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 nvvertparse.c
|
|
* NVIDIA vertex program parser.
|
|
* \author Brian Paul
|
|
*/
|
|
|
|
/*
|
|
* Regarding GL_NV_vertex_program, GL_NV_vertex_program1_1:
|
|
*
|
|
* 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 "main/glheader.h"
|
|
#include "main/context.h"
|
|
#include "main/imports.h"
|
|
#include "main/nvprogram.h"
|
|
#include "nvvertparse.h"
|
|
#include "prog_instruction.h"
|
|
#include "prog_parameter.h"
|
|
#include "prog_print.h"
|
|
#include "program.h"
|
|
|
|
|
|
/**
|
|
* Current parsing state. This structure is passed among the parsing
|
|
* functions and keeps track of the current parser position and various
|
|
* program attributes.
|
|
*/
|
|
struct parse_state {
|
|
GLcontext *ctx;
|
|
const GLubyte *start;
|
|
const GLubyte *pos;
|
|
const GLubyte *curLine;
|
|
GLboolean isStateProgram;
|
|
GLboolean isPositionInvariant;
|
|
GLboolean isVersion1_1;
|
|
GLbitfield inputsRead;
|
|
GLbitfield outputsWritten;
|
|
GLboolean anyProgRegsWritten;
|
|
GLuint numInst; /* number of instructions parsed */
|
|
};
|
|
|
|
|
|
/*
|
|
* 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);
|
|
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]; \
|
|
sprintf(err, "%s %s", msg1, msg2); \
|
|
record_error(parseState, err, __LINE__); \
|
|
return GL_FALSE; \
|
|
} while(0)
|
|
|
|
|
|
|
|
|
|
|
|
static GLboolean IsLetter(GLubyte b)
|
|
{
|
|
return (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z');
|
|
}
|
|
|
|
|
|
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) strlen((const char *) token);
|
|
parseState->pos += (i - len);
|
|
return GL_TRUE;
|
|
}
|
|
|
|
|
|
/**
|
|
* Try to match 'pattern' as the next token after any whitespace/comments.
|
|
* Advance the current parsing position only if we match the pattern.
|
|
* \return GL_TRUE if pattern is matched, GL_FALSE otherwise.
|
|
*/
|
|
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 const char *InputRegisters[MAX_NV_VERTEX_PROGRAM_INPUTS + 1] = {
|
|
"OPOS", "WGHT", "NRML", "COL0", "COL1", "FOGC", "6", "7",
|
|
"TEX0", "TEX1", "TEX2", "TEX3", "TEX4", "TEX5", "TEX6", "TEX7", NULL
|
|
};
|
|
|
|
static const char *OutputRegisters[MAX_NV_VERTEX_PROGRAM_OUTPUTS + 1] = {
|
|
"HPOS", "COL0", "COL1", "FOGC",
|
|
"TEX0", "TEX1", "TEX2", "TEX3", "TEX4", "TEX5", "TEX6", "TEX7",
|
|
"PSIZ", "BFC0", "BFC1", NULL
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
* Parse a temporary register: Rnn
|
|
*/
|
|
static GLboolean
|
|
Parse_TempReg(struct parse_state *parseState, GLint *tempRegNum)
|
|
{
|
|
GLubyte token[100];
|
|
|
|
/* Should be 'R##' */
|
|
if (!Parse_Token(parseState, token))
|
|
RETURN_ERROR;
|
|
if (token[0] != 'R')
|
|
RETURN_ERROR1("Expected R##");
|
|
|
|
if (IsDigit(token[1])) {
|
|
GLint reg = atoi((char *) (token + 1));
|
|
if (reg >= MAX_NV_VERTEX_PROGRAM_TEMPS)
|
|
RETURN_ERROR1("Bad temporary register name");
|
|
*tempRegNum = reg;
|
|
}
|
|
else {
|
|
RETURN_ERROR1("Bad temporary register name");
|
|
}
|
|
|
|
return GL_TRUE;
|
|
}
|
|
|
|
|
|
/**
|
|
* Parse address register "A0.x"
|
|
*/
|
|
static GLboolean
|
|
Parse_AddrReg(struct parse_state *parseState)
|
|
{
|
|
/* match 'A0' */
|
|
if (!Parse_String(parseState, "A0"))
|
|
RETURN_ERROR;
|
|
|
|
/* match '.' */
|
|
if (!Parse_String(parseState, "."))
|
|
RETURN_ERROR;
|
|
|
|
/* match 'x' */
|
|
if (!Parse_String(parseState, "x"))
|
|
RETURN_ERROR;
|
|
|
|
return GL_TRUE;
|
|
}
|
|
|
|
|
|
/**
|
|
* Parse absolute program parameter register "c[##]"
|
|
*/
|
|
static GLboolean
|
|
Parse_AbsParamReg(struct parse_state *parseState, GLint *regNum)
|
|
{
|
|
GLubyte token[100];
|
|
|
|
if (!Parse_String(parseState, "c"))
|
|
RETURN_ERROR;
|
|
|
|
if (!Parse_String(parseState, "["))
|
|
RETURN_ERROR;
|
|
|
|
if (!Parse_Token(parseState, token))
|
|
RETURN_ERROR;
|
|
|
|
if (IsDigit(token[0])) {
|
|
/* a numbered program parameter register */
|
|
GLint reg = atoi((char *) token);
|
|
if (reg >= MAX_NV_VERTEX_PROGRAM_PARAMS)
|
|
RETURN_ERROR1("Bad program parameter number");
|
|
*regNum = reg;
|
|
}
|
|
else {
|
|
RETURN_ERROR;
|
|
}
|
|
|
|
if (!Parse_String(parseState, "]"))
|
|
RETURN_ERROR;
|
|
|
|
return GL_TRUE;
|
|
}
|
|
|
|
|
|
static GLboolean
|
|
Parse_ParamReg(struct parse_state *parseState, struct prog_src_register *srcReg)
|
|
{
|
|
GLubyte token[100];
|
|
|
|
if (!Parse_String(parseState, "c"))
|
|
RETURN_ERROR;
|
|
|
|
if (!Parse_String(parseState, "["))
|
|
RETURN_ERROR;
|
|
|
|
if (!Peek_Token(parseState, token))
|
|
RETURN_ERROR;
|
|
|
|
if (IsDigit(token[0])) {
|
|
/* a numbered program parameter register */
|
|
GLint reg;
|
|
(void) Parse_Token(parseState, token);
|
|
reg = atoi((char *) token);
|
|
if (reg >= MAX_NV_VERTEX_PROGRAM_PARAMS)
|
|
RETURN_ERROR1("Bad program parameter number");
|
|
srcReg->File = PROGRAM_ENV_PARAM;
|
|
srcReg->Index = reg;
|
|
}
|
|
else if (strcmp((const char *) token, "A0") == 0) {
|
|
/* address register "A0.x" */
|
|
if (!Parse_AddrReg(parseState))
|
|
RETURN_ERROR;
|
|
|
|
srcReg->RelAddr = GL_TRUE;
|
|
srcReg->File = PROGRAM_ENV_PARAM;
|
|
/* Look for +/-N offset */
|
|
if (!Peek_Token(parseState, token))
|
|
RETURN_ERROR;
|
|
|
|
if (token[0] == '-' || token[0] == '+') {
|
|
const GLubyte sign = token[0];
|
|
(void) Parse_Token(parseState, token); /* consume +/- */
|
|
|
|
/* an integer should be next */
|
|
if (!Parse_Token(parseState, token))
|
|
RETURN_ERROR;
|
|
|
|
if (IsDigit(token[0])) {
|
|
const GLint k = atoi((char *) token);
|
|
if (sign == '-') {
|
|
if (k > 64)
|
|
RETURN_ERROR1("Bad address offset");
|
|
srcReg->Index = -k;
|
|
}
|
|
else {
|
|
if (k > 63)
|
|
RETURN_ERROR1("Bad address offset");
|
|
srcReg->Index = k;
|
|
}
|
|
}
|
|
else {
|
|
RETURN_ERROR;
|
|
}
|
|
}
|
|
else {
|
|
/* probably got a ']', catch it below */
|
|
}
|
|
}
|
|
else {
|
|
RETURN_ERROR;
|
|
}
|
|
|
|
/* Match closing ']' */
|
|
if (!Parse_String(parseState, "]"))
|
|
RETURN_ERROR;
|
|
|
|
return GL_TRUE;
|
|
}
|
|
|
|
|
|
/**
|
|
* Parse v[#] or v[<name>]
|
|
*/
|
|
static GLboolean
|
|
Parse_AttribReg(struct parse_state *parseState, GLint *tempRegNum)
|
|
{
|
|
GLubyte token[100];
|
|
GLint j;
|
|
|
|
/* Match 'v' */
|
|
if (!Parse_String(parseState, "v"))
|
|
RETURN_ERROR;
|
|
|
|
/* Match '[' */
|
|
if (!Parse_String(parseState, "["))
|
|
RETURN_ERROR;
|
|
|
|
/* match number or named register */
|
|
if (!Parse_Token(parseState, token))
|
|
RETURN_ERROR;
|
|
|
|
if (parseState->isStateProgram && token[0] != '0')
|
|
RETURN_ERROR1("Only v[0] accessible in vertex state programs");
|
|
|
|
if (IsDigit(token[0])) {
|
|
GLint reg = atoi((char *) token);
|
|
if (reg >= MAX_NV_VERTEX_PROGRAM_INPUTS)
|
|
RETURN_ERROR1("Bad vertex attribute register name");
|
|
*tempRegNum = reg;
|
|
}
|
|
else {
|
|
for (j = 0; InputRegisters[j]; j++) {
|
|
if (strcmp((const char *) token, InputRegisters[j]) == 0) {
|
|
*tempRegNum = j;
|
|
break;
|
|
}
|
|
}
|
|
if (!InputRegisters[j]) {
|
|
/* unknown input register label */
|
|
RETURN_ERROR2("Bad register name", token);
|
|
}
|
|
}
|
|
|
|
/* Match '[' */
|
|
if (!Parse_String(parseState, "]"))
|
|
RETURN_ERROR;
|
|
|
|
return GL_TRUE;
|
|
}
|
|
|
|
|
|
static GLboolean
|
|
Parse_OutputReg(struct parse_state *parseState, GLint *outputRegNum)
|
|
{
|
|
GLubyte token[100];
|
|
GLint start, j;
|
|
|
|
/* Match 'o' */
|
|
if (!Parse_String(parseState, "o"))
|
|
RETURN_ERROR;
|
|
|
|
/* Match '[' */
|
|
if (!Parse_String(parseState, "["))
|
|
RETURN_ERROR;
|
|
|
|
/* Get output reg name */
|
|
if (!Parse_Token(parseState, token))
|
|
RETURN_ERROR;
|
|
|
|
if (parseState->isPositionInvariant)
|
|
start = 1; /* skip HPOS register name */
|
|
else
|
|
start = 0;
|
|
|
|
/* try to match an output register name */
|
|
for (j = start; OutputRegisters[j]; j++) {
|
|
if (strcmp((const char *) token, OutputRegisters[j]) == 0) {
|
|
*outputRegNum = j;
|
|
break;
|
|
}
|
|
}
|
|
if (!OutputRegisters[j])
|
|
RETURN_ERROR1("Unrecognized 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> or o[n] */
|
|
if (!Peek_Token(parseState, token))
|
|
RETURN_ERROR;
|
|
|
|
if (token[0] == 'R') {
|
|
/* a temporary register */
|
|
dstReg->File = PROGRAM_TEMPORARY;
|
|
if (!Parse_TempReg(parseState, &idx))
|
|
RETURN_ERROR;
|
|
dstReg->Index = idx;
|
|
}
|
|
else if (!parseState->isStateProgram && token[0] == 'o') {
|
|
/* an output register */
|
|
dstReg->File = PROGRAM_OUTPUT;
|
|
if (!Parse_OutputReg(parseState, &idx))
|
|
RETURN_ERROR;
|
|
dstReg->Index = idx;
|
|
}
|
|
else if (parseState->isStateProgram && token[0] == 'c' &&
|
|
parseState->isStateProgram) {
|
|
/* absolute program parameter register */
|
|
/* Only valid for vertex state programs */
|
|
dstReg->File = PROGRAM_ENV_PARAM;
|
|
if (!Parse_AbsParamReg(parseState, &idx))
|
|
RETURN_ERROR;
|
|
dstReg->Index = idx;
|
|
}
|
|
else {
|
|
RETURN_ERROR1("Bad destination register name");
|
|
}
|
|
|
|
/* Parse optional write mask */
|
|
if (!Peek_Token(parseState, token))
|
|
RETURN_ERROR;
|
|
|
|
if (token[0] == '.') {
|
|
/* got a mask */
|
|
GLint k = 0;
|
|
|
|
if (!Parse_String(parseState, "."))
|
|
RETURN_ERROR;
|
|
|
|
if (!Parse_Token(parseState, token))
|
|
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("Bad writemask character");
|
|
}
|
|
return GL_TRUE;
|
|
}
|
|
else {
|
|
dstReg->WriteMask = WRITEMASK_XYZW;
|
|
return GL_TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
static GLboolean
|
|
Parse_SwizzleSrcReg(struct parse_state *parseState, struct prog_src_register *srcReg)
|
|
{
|
|
GLubyte token[100];
|
|
GLint idx;
|
|
|
|
srcReg->RelAddr = GL_FALSE;
|
|
|
|
/* check for '-' */
|
|
if (!Peek_Token(parseState, token))
|
|
RETURN_ERROR;
|
|
if (token[0] == '-') {
|
|
(void) Parse_String(parseState, "-");
|
|
srcReg->Negate = NEGATE_XYZW;
|
|
if (!Peek_Token(parseState, token))
|
|
RETURN_ERROR;
|
|
}
|
|
else {
|
|
srcReg->Negate = NEGATE_NONE;
|
|
}
|
|
|
|
/* Src reg can be R<n>, c[n], c[n +/- offset], or a named vertex attrib */
|
|
if (token[0] == 'R') {
|
|
srcReg->File = PROGRAM_TEMPORARY;
|
|
if (!Parse_TempReg(parseState, &idx))
|
|
RETURN_ERROR;
|
|
srcReg->Index = idx;
|
|
}
|
|
else if (token[0] == 'c') {
|
|
if (!Parse_ParamReg(parseState, srcReg))
|
|
RETURN_ERROR;
|
|
}
|
|
else if (token[0] == 'v') {
|
|
srcReg->File = PROGRAM_INPUT;
|
|
if (!Parse_AttribReg(parseState, &idx))
|
|
RETURN_ERROR;
|
|
srcReg->Index = idx;
|
|
}
|
|
else {
|
|
RETURN_ERROR2("Bad source register name", token);
|
|
}
|
|
|
|
/* init swizzle fields */
|
|
srcReg->Swizzle = SWIZZLE_NOOP;
|
|
|
|
/* Look for optional swizzle suffix */
|
|
if (!Peek_Token(parseState, token))
|
|
RETURN_ERROR;
|
|
if (token[0] == '.') {
|
|
(void) Parse_String(parseState, "."); /* consume . */
|
|
|
|
if (!Parse_Token(parseState, token))
|
|
RETURN_ERROR;
|
|
|
|
if (token[1] == 0) {
|
|
/* single letter swizzle */
|
|
if (token[0] == 'x')
|
|
srcReg->Swizzle = SWIZZLE_XXXX;
|
|
else if (token[0] == 'y')
|
|
srcReg->Swizzle = SWIZZLE_YYYY;
|
|
else if (token[0] == 'z')
|
|
srcReg->Swizzle = SWIZZLE_ZZZZ;
|
|
else if (token[0] == 'w')
|
|
srcReg->Swizzle = SWIZZLE_WWWW;
|
|
else
|
|
RETURN_ERROR1("Expected x, y, z, or w");
|
|
}
|
|
else {
|
|
/* 2, 3 or 4-component swizzle */
|
|
GLint k;
|
|
|
|
srcReg->Swizzle = 0;
|
|
|
|
for (k = 0; token[k] && k < 5; k++) {
|
|
if (token[k] == 'x')
|
|
srcReg->Swizzle |= 0 << (k*3);
|
|
else if (token[k] == 'y')
|
|
srcReg->Swizzle |= 1 << (k*3);
|
|
else if (token[k] == 'z')
|
|
srcReg->Swizzle |= 2 << (k*3);
|
|
else if (token[k] == 'w')
|
|
srcReg->Swizzle |= 3 << (k*3);
|
|
else
|
|
RETURN_ERROR;
|
|
}
|
|
if (k >= 5)
|
|
RETURN_ERROR;
|
|
}
|
|
}
|
|
|
|
return GL_TRUE;
|
|
}
|
|
|
|
|
|
static GLboolean
|
|
Parse_ScalarSrcReg(struct parse_state *parseState, struct prog_src_register *srcReg)
|
|
{
|
|
GLubyte token[100];
|
|
GLint idx;
|
|
|
|
srcReg->RelAddr = GL_FALSE;
|
|
|
|
/* check for '-' */
|
|
if (!Peek_Token(parseState, token))
|
|
RETURN_ERROR;
|
|
if (token[0] == '-') {
|
|
srcReg->Negate = NEGATE_XYZW;
|
|
(void) Parse_String(parseState, "-"); /* consume '-' */
|
|
if (!Peek_Token(parseState, token))
|
|
RETURN_ERROR;
|
|
}
|
|
else {
|
|
srcReg->Negate = NEGATE_NONE;
|
|
}
|
|
|
|
/* Src reg can be R<n>, c[n], c[n +/- offset], or a named vertex attrib */
|
|
if (token[0] == 'R') {
|
|
srcReg->File = PROGRAM_TEMPORARY;
|
|
if (!Parse_TempReg(parseState, &idx))
|
|
RETURN_ERROR;
|
|
srcReg->Index = idx;
|
|
}
|
|
else if (token[0] == 'c') {
|
|
if (!Parse_ParamReg(parseState, srcReg))
|
|
RETURN_ERROR;
|
|
}
|
|
else if (token[0] == 'v') {
|
|
srcReg->File = PROGRAM_INPUT;
|
|
if (!Parse_AttribReg(parseState, &idx))
|
|
RETURN_ERROR;
|
|
srcReg->Index = idx;
|
|
}
|
|
else {
|
|
RETURN_ERROR2("Bad source register name", token);
|
|
}
|
|
|
|
/* Look for .[xyzw] suffix */
|
|
if (!Parse_String(parseState, "."))
|
|
RETURN_ERROR;
|
|
|
|
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("Bad scalar source suffix");
|
|
}
|
|
|
|
return GL_TRUE;
|
|
}
|
|
|
|
|
|
static GLint
|
|
Parse_UnaryOpInstruction(struct parse_state *parseState,
|
|
struct prog_instruction *inst,
|
|
enum prog_opcode opcode)
|
|
{
|
|
if (opcode == OPCODE_ABS && !parseState->isVersion1_1)
|
|
RETURN_ERROR1("ABS illegal for vertex program 1.0");
|
|
|
|
inst->Opcode = opcode;
|
|
|
|
/* dest reg */
|
|
if (!Parse_MaskedDstReg(parseState, &inst->DstReg))
|
|
RETURN_ERROR;
|
|
|
|
/* comma */
|
|
if (!Parse_String(parseState, ","))
|
|
RETURN_ERROR;
|
|
|
|
/* src arg */
|
|
if (!Parse_SwizzleSrcReg(parseState, &inst->SrcReg[0]))
|
|
RETURN_ERROR;
|
|
|
|
/* semicolon */
|
|
if (!Parse_String(parseState, ";"))
|
|
RETURN_ERROR;
|
|
|
|
return GL_TRUE;
|
|
}
|
|
|
|
|
|
static GLboolean
|
|
Parse_BiOpInstruction(struct parse_state *parseState,
|
|
struct prog_instruction *inst,
|
|
enum prog_opcode opcode)
|
|
{
|
|
if (opcode == OPCODE_DPH && !parseState->isVersion1_1)
|
|
RETURN_ERROR1("DPH illegal for vertex program 1.0");
|
|
if (opcode == OPCODE_SUB && !parseState->isVersion1_1)
|
|
RETURN_ERROR1("SUB illegal for vertex program 1.0");
|
|
|
|
inst->Opcode = opcode;
|
|
|
|
/* dest reg */
|
|
if (!Parse_MaskedDstReg(parseState, &inst->DstReg))
|
|
RETURN_ERROR;
|
|
|
|
/* comma */
|
|
if (!Parse_String(parseState, ","))
|
|
RETURN_ERROR;
|
|
|
|
/* first src arg */
|
|
if (!Parse_SwizzleSrcReg(parseState, &inst->SrcReg[0]))
|
|
RETURN_ERROR;
|
|
|
|
/* comma */
|
|
if (!Parse_String(parseState, ","))
|
|
RETURN_ERROR;
|
|
|
|
/* second src arg */
|
|
if (!Parse_SwizzleSrcReg(parseState, &inst->SrcReg[1]))
|
|
RETURN_ERROR;
|
|
|
|
/* semicolon */
|
|
if (!Parse_String(parseState, ";"))
|
|
RETURN_ERROR;
|
|
|
|
/* make sure we don't reference more than one program parameter register */
|
|
if (inst->SrcReg[0].File == PROGRAM_ENV_PARAM &&
|
|
inst->SrcReg[1].File == PROGRAM_ENV_PARAM &&
|
|
inst->SrcReg[0].Index != inst->SrcReg[1].Index)
|
|
RETURN_ERROR1("Can't reference two program parameter registers");
|
|
|
|
/* make sure we don't reference more than one vertex attribute register */
|
|
if (inst->SrcReg[0].File == PROGRAM_INPUT &&
|
|
inst->SrcReg[1].File == PROGRAM_INPUT &&
|
|
inst->SrcReg[0].Index != inst->SrcReg[1].Index)
|
|
RETURN_ERROR1("Can't reference two vertex attribute registers");
|
|
|
|
return GL_TRUE;
|
|
}
|
|
|
|
|
|
static GLboolean
|
|
Parse_TriOpInstruction(struct parse_state *parseState,
|
|
struct prog_instruction *inst,
|
|
enum prog_opcode opcode)
|
|
{
|
|
inst->Opcode = opcode;
|
|
|
|
/* dest reg */
|
|
if (!Parse_MaskedDstReg(parseState, &inst->DstReg))
|
|
RETURN_ERROR;
|
|
|
|
/* comma */
|
|
if (!Parse_String(parseState, ","))
|
|
RETURN_ERROR;
|
|
|
|
/* first src arg */
|
|
if (!Parse_SwizzleSrcReg(parseState, &inst->SrcReg[0]))
|
|
RETURN_ERROR;
|
|
|
|
/* comma */
|
|
if (!Parse_String(parseState, ","))
|
|
RETURN_ERROR;
|
|
|
|
/* second src arg */
|
|
if (!Parse_SwizzleSrcReg(parseState, &inst->SrcReg[1]))
|
|
RETURN_ERROR;
|
|
|
|
/* comma */
|
|
if (!Parse_String(parseState, ","))
|
|
RETURN_ERROR;
|
|
|
|
/* third src arg */
|
|
if (!Parse_SwizzleSrcReg(parseState, &inst->SrcReg[2]))
|
|
RETURN_ERROR;
|
|
|
|
/* semicolon */
|
|
if (!Parse_String(parseState, ";"))
|
|
RETURN_ERROR;
|
|
|
|
/* make sure we don't reference more than one program parameter register */
|
|
if ((inst->SrcReg[0].File == PROGRAM_ENV_PARAM &&
|
|
inst->SrcReg[1].File == PROGRAM_ENV_PARAM &&
|
|
inst->SrcReg[0].Index != inst->SrcReg[1].Index) ||
|
|
(inst->SrcReg[0].File == PROGRAM_ENV_PARAM &&
|
|
inst->SrcReg[2].File == PROGRAM_ENV_PARAM &&
|
|
inst->SrcReg[0].Index != inst->SrcReg[2].Index) ||
|
|
(inst->SrcReg[1].File == PROGRAM_ENV_PARAM &&
|
|
inst->SrcReg[2].File == PROGRAM_ENV_PARAM &&
|
|
inst->SrcReg[1].Index != inst->SrcReg[2].Index))
|
|
RETURN_ERROR1("Can only reference one program register");
|
|
|
|
/* make sure we don't reference more than one vertex attribute register */
|
|
if ((inst->SrcReg[0].File == PROGRAM_INPUT &&
|
|
inst->SrcReg[1].File == PROGRAM_INPUT &&
|
|
inst->SrcReg[0].Index != inst->SrcReg[1].Index) ||
|
|
(inst->SrcReg[0].File == PROGRAM_INPUT &&
|
|
inst->SrcReg[2].File == PROGRAM_INPUT &&
|
|
inst->SrcReg[0].Index != inst->SrcReg[2].Index) ||
|
|
(inst->SrcReg[1].File == PROGRAM_INPUT &&
|
|
inst->SrcReg[2].File == PROGRAM_INPUT &&
|
|
inst->SrcReg[1].Index != inst->SrcReg[2].Index))
|
|
RETURN_ERROR1("Can only reference one input register");
|
|
|
|
return GL_TRUE;
|
|
}
|
|
|
|
|
|
static GLboolean
|
|
Parse_ScalarInstruction(struct parse_state *parseState,
|
|
struct prog_instruction *inst,
|
|
enum prog_opcode opcode)
|
|
{
|
|
if (opcode == OPCODE_RCC && !parseState->isVersion1_1)
|
|
RETURN_ERROR1("RCC illegal for vertex program 1.0");
|
|
|
|
inst->Opcode = opcode;
|
|
|
|
/* dest reg */
|
|
if (!Parse_MaskedDstReg(parseState, &inst->DstReg))
|
|
RETURN_ERROR;
|
|
|
|
/* comma */
|
|
if (!Parse_String(parseState, ","))
|
|
RETURN_ERROR;
|
|
|
|
/* first src arg */
|
|
if (!Parse_ScalarSrcReg(parseState, &inst->SrcReg[0]))
|
|
RETURN_ERROR;
|
|
|
|
/* semicolon */
|
|
if (!Parse_String(parseState, ";"))
|
|
RETURN_ERROR;
|
|
|
|
return GL_TRUE;
|
|
}
|
|
|
|
|
|
static GLboolean
|
|
Parse_AddressInstruction(struct parse_state *parseState, struct prog_instruction *inst)
|
|
{
|
|
inst->Opcode = OPCODE_ARL;
|
|
|
|
/* Make ARB_vp backends happy */
|
|
inst->DstReg.File = PROGRAM_ADDRESS;
|
|
inst->DstReg.WriteMask = WRITEMASK_X;
|
|
inst->DstReg.Index = 0;
|
|
|
|
/* dest A0 reg */
|
|
if (!Parse_AddrReg(parseState))
|
|
RETURN_ERROR;
|
|
|
|
/* comma */
|
|
if (!Parse_String(parseState, ","))
|
|
RETURN_ERROR;
|
|
|
|
/* parse src reg */
|
|
if (!Parse_ScalarSrcReg(parseState, &inst->SrcReg[0]))
|
|
RETURN_ERROR;
|
|
|
|
/* semicolon */
|
|
if (!Parse_String(parseState, ";"))
|
|
RETURN_ERROR;
|
|
|
|
return GL_TRUE;
|
|
}
|
|
|
|
|
|
static GLboolean
|
|
Parse_EndInstruction(struct parse_state *parseState, struct prog_instruction *inst)
|
|
{
|
|
GLubyte token[100];
|
|
|
|
inst->Opcode = OPCODE_END;
|
|
|
|
/* this should fail! */
|
|
if (Parse_Token(parseState, token))
|
|
RETURN_ERROR2("Unexpected token after END:", token);
|
|
else
|
|
return GL_TRUE;
|
|
}
|
|
|
|
|
|
/**
|
|
* The PRINT instruction is Mesa-specific and is meant as a debugging aid for
|
|
* the vertex program developer.
|
|
* The NV_vertex_program extension grammar is modified as follows:
|
|
*
|
|
* <instruction> ::= <ARL-instruction>
|
|
* | ...
|
|
* | <PRINT-instruction>
|
|
*
|
|
* <PRINT-instruction> ::= "PRINT" <string literal>
|
|
* | "PRINT" <string literal> "," <srcReg>
|
|
* | "PRINT" <string literal> "," <dstReg>
|
|
*/
|
|
static GLboolean
|
|
Parse_PrintInstruction(struct parse_state *parseState, struct prog_instruction *inst)
|
|
{
|
|
const GLubyte *str;
|
|
GLubyte *msg;
|
|
GLuint len;
|
|
GLubyte token[100];
|
|
struct prog_src_register *srcReg = &inst->SrcReg[0];
|
|
GLint idx;
|
|
|
|
inst->Opcode = OPCODE_PRINT;
|
|
|
|
/* The first argument is a literal string 'just like this' */
|
|
if (!Parse_String(parseState, "'"))
|
|
RETURN_ERROR;
|
|
|
|
str = parseState->pos;
|
|
for (len = 0; str[len] != '\''; len++) /* find closing quote */
|
|
;
|
|
parseState->pos += len + 1;
|
|
msg = (GLubyte*) malloc(len + 1);
|
|
|
|
memcpy(msg, str, len);
|
|
msg[len] = 0;
|
|
inst->Data = msg;
|
|
|
|
/* comma */
|
|
if (Parse_String(parseState, ",")) {
|
|
|
|
/* The second argument is a register name */
|
|
if (!Peek_Token(parseState, token))
|
|
RETURN_ERROR;
|
|
|
|
srcReg->RelAddr = GL_FALSE;
|
|
srcReg->Negate = NEGATE_NONE;
|
|
srcReg->Swizzle = SWIZZLE_NOOP;
|
|
|
|
/* Register can be R<n>, c[n], c[n +/- offset], a named vertex attrib,
|
|
* or an o[n] output register.
|
|
*/
|
|
if (token[0] == 'R') {
|
|
srcReg->File = PROGRAM_TEMPORARY;
|
|
if (!Parse_TempReg(parseState, &idx))
|
|
RETURN_ERROR;
|
|
srcReg->Index = idx;
|
|
}
|
|
else if (token[0] == 'c') {
|
|
srcReg->File = PROGRAM_ENV_PARAM;
|
|
if (!Parse_ParamReg(parseState, srcReg))
|
|
RETURN_ERROR;
|
|
}
|
|
else if (token[0] == 'v') {
|
|
srcReg->File = PROGRAM_INPUT;
|
|
if (!Parse_AttribReg(parseState, &idx))
|
|
RETURN_ERROR;
|
|
srcReg->Index = idx;
|
|
}
|
|
else if (token[0] == 'o') {
|
|
srcReg->File = PROGRAM_OUTPUT;
|
|
if (!Parse_OutputReg(parseState, &idx))
|
|
RETURN_ERROR;
|
|
srcReg->Index = idx;
|
|
}
|
|
else {
|
|
RETURN_ERROR2("Bad source register name", token);
|
|
}
|
|
}
|
|
else {
|
|
srcReg->File = PROGRAM_UNDEFINED;
|
|
}
|
|
|
|
/* semicolon */
|
|
if (!Parse_String(parseState, ";"))
|
|
RETURN_ERROR;
|
|
|
|
return GL_TRUE;
|
|
}
|
|
|
|
|
|
static GLboolean
|
|
Parse_OptionSequence(struct parse_state *parseState,
|
|
struct prog_instruction program[])
|
|
{
|
|
(void) program;
|
|
while (1) {
|
|
if (!Parse_String(parseState, "OPTION"))
|
|
return GL_TRUE; /* ok, not an OPTION statement */
|
|
if (Parse_String(parseState, "NV_position_invariant")) {
|
|
parseState->isPositionInvariant = GL_TRUE;
|
|
}
|
|
else {
|
|
RETURN_ERROR1("unexpected OPTION statement");
|
|
}
|
|
if (!Parse_String(parseState, ";"))
|
|
return GL_FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
static GLboolean
|
|
Parse_InstructionSequence(struct parse_state *parseState,
|
|
struct prog_instruction program[])
|
|
{
|
|
while (1) {
|
|
struct prog_instruction *inst = program + parseState->numInst;
|
|
|
|
/* Initialize the instruction */
|
|
_mesa_init_instructions(inst, 1);
|
|
|
|
if (Parse_String(parseState, "MOV")) {
|
|
if (!Parse_UnaryOpInstruction(parseState, inst, OPCODE_MOV))
|
|
RETURN_ERROR;
|
|
}
|
|
else if (Parse_String(parseState, "LIT")) {
|
|
if (!Parse_UnaryOpInstruction(parseState, inst, OPCODE_LIT))
|
|
RETURN_ERROR;
|
|
}
|
|
else if (Parse_String(parseState, "ABS")) {
|
|
if (!Parse_UnaryOpInstruction(parseState, inst, OPCODE_ABS))
|
|
RETURN_ERROR;
|
|
}
|
|
else if (Parse_String(parseState, "MUL")) {
|
|
if (!Parse_BiOpInstruction(parseState, inst, OPCODE_MUL))
|
|
RETURN_ERROR;
|
|
}
|
|
else if (Parse_String(parseState, "ADD")) {
|
|
if (!Parse_BiOpInstruction(parseState, inst, OPCODE_ADD))
|
|
RETURN_ERROR;
|
|
}
|
|
else if (Parse_String(parseState, "DP3")) {
|
|
if (!Parse_BiOpInstruction(parseState, inst, OPCODE_DP3))
|
|
RETURN_ERROR;
|
|
}
|
|
else if (Parse_String(parseState, "DP4")) {
|
|
if (!Parse_BiOpInstruction(parseState, inst, OPCODE_DP4))
|
|
RETURN_ERROR;
|
|
}
|
|
else if (Parse_String(parseState, "DST")) {
|
|
if (!Parse_BiOpInstruction(parseState, inst, OPCODE_DST))
|
|
RETURN_ERROR;
|
|
}
|
|
else if (Parse_String(parseState, "MIN")) {
|
|
if (!Parse_BiOpInstruction(parseState, inst, OPCODE_MIN))
|
|
RETURN_ERROR;
|
|
}
|
|
else if (Parse_String(parseState, "MAX")) {
|
|
if (!Parse_BiOpInstruction(parseState, inst, OPCODE_MAX))
|
|
RETURN_ERROR;
|
|
}
|
|
else if (Parse_String(parseState, "SLT")) {
|
|
if (!Parse_BiOpInstruction(parseState, inst, OPCODE_SLT))
|
|
RETURN_ERROR;
|
|
}
|
|
else if (Parse_String(parseState, "SGE")) {
|
|
if (!Parse_BiOpInstruction(parseState, inst, OPCODE_SGE))
|
|
RETURN_ERROR;
|
|
}
|
|
else if (Parse_String(parseState, "DPH")) {
|
|
if (!Parse_BiOpInstruction(parseState, inst, OPCODE_DPH))
|
|
RETURN_ERROR;
|
|
}
|
|
else if (Parse_String(parseState, "SUB")) {
|
|
if (!Parse_BiOpInstruction(parseState, inst, OPCODE_SUB))
|
|
RETURN_ERROR;
|
|
}
|
|
else if (Parse_String(parseState, "MAD")) {
|
|
if (!Parse_TriOpInstruction(parseState, inst, OPCODE_MAD))
|
|
RETURN_ERROR;
|
|
}
|
|
else if (Parse_String(parseState, "RCP")) {
|
|
if (!Parse_ScalarInstruction(parseState, inst, OPCODE_RCP))
|
|
RETURN_ERROR;
|
|
}
|
|
else if (Parse_String(parseState, "RSQ")) {
|
|
if (!Parse_ScalarInstruction(parseState, inst, OPCODE_RSQ))
|
|
RETURN_ERROR;
|
|
}
|
|
else if (Parse_String(parseState, "EXP")) {
|
|
if (!Parse_ScalarInstruction(parseState, inst, OPCODE_EXP))
|
|
RETURN_ERROR;
|
|
}
|
|
else if (Parse_String(parseState, "LOG")) {
|
|
if (!Parse_ScalarInstruction(parseState, inst, OPCODE_LOG))
|
|
RETURN_ERROR;
|
|
}
|
|
else if (Parse_String(parseState, "RCC")) {
|
|
if (!Parse_ScalarInstruction(parseState, inst, OPCODE_RCC))
|
|
RETURN_ERROR;
|
|
}
|
|
else if (Parse_String(parseState, "ARL")) {
|
|
if (!Parse_AddressInstruction(parseState, inst))
|
|
RETURN_ERROR;
|
|
}
|
|
else if (Parse_String(parseState, "PRINT")) {
|
|
if (!Parse_PrintInstruction(parseState, inst))
|
|
RETURN_ERROR;
|
|
}
|
|
else if (Parse_String(parseState, "END")) {
|
|
if (!Parse_EndInstruction(parseState, inst))
|
|
RETURN_ERROR;
|
|
else {
|
|
parseState->numInst++;
|
|
return GL_TRUE; /* all done */
|
|
}
|
|
}
|
|
else {
|
|
/* bad instruction name */
|
|
RETURN_ERROR1("Unexpected token");
|
|
}
|
|
|
|
/* examine input/output registers */
|
|
if (inst->DstReg.File == PROGRAM_OUTPUT)
|
|
parseState->outputsWritten |= (1 << inst->DstReg.Index);
|
|
else if (inst->DstReg.File == PROGRAM_ENV_PARAM)
|
|
parseState->anyProgRegsWritten = GL_TRUE;
|
|
|
|
if (inst->SrcReg[0].File == PROGRAM_INPUT)
|
|
parseState->inputsRead |= (1 << inst->SrcReg[0].Index);
|
|
if (inst->SrcReg[1].File == PROGRAM_INPUT)
|
|
parseState->inputsRead |= (1 << inst->SrcReg[1].Index);
|
|
if (inst->SrcReg[2].File == PROGRAM_INPUT)
|
|
parseState->inputsRead |= (1 << inst->SrcReg[2].Index);
|
|
|
|
parseState->numInst++;
|
|
|
|
if (parseState->numInst >= MAX_NV_VERTEX_PROGRAM_INSTRUCTIONS)
|
|
RETURN_ERROR1("Program too long");
|
|
}
|
|
|
|
RETURN_ERROR;
|
|
}
|
|
|
|
|
|
static GLboolean
|
|
Parse_Program(struct parse_state *parseState,
|
|
struct prog_instruction instBuffer[])
|
|
{
|
|
if (parseState->isVersion1_1) {
|
|
if (!Parse_OptionSequence(parseState, instBuffer)) {
|
|
return GL_FALSE;
|
|
}
|
|
}
|
|
return Parse_InstructionSequence(parseState, instBuffer);
|
|
}
|
|
|
|
|
|
/**
|
|
* 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_vertex_program(GLcontext *ctx, GLenum dstTarget,
|
|
const GLubyte *str, GLsizei len,
|
|
struct gl_vertex_program *program)
|
|
{
|
|
struct parse_state parseState;
|
|
struct prog_instruction instBuffer[MAX_NV_VERTEX_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 */
|
|
parseState.ctx = ctx;
|
|
parseState.start = programString;
|
|
parseState.isPositionInvariant = GL_FALSE;
|
|
parseState.isVersion1_1 = GL_FALSE;
|
|
parseState.numInst = 0;
|
|
parseState.inputsRead = 0;
|
|
parseState.outputsWritten = 0;
|
|
parseState.anyProgRegsWritten = GL_FALSE;
|
|
|
|
/* Reset error state */
|
|
_mesa_set_program_error(ctx, -1, NULL);
|
|
|
|
/* check the program header */
|
|
if (strncmp((const char *) programString, "!!VP1.0", 7) == 0) {
|
|
target = GL_VERTEX_PROGRAM_NV;
|
|
parseState.pos = programString + 7;
|
|
parseState.isStateProgram = GL_FALSE;
|
|
}
|
|
else if (strncmp((const char *) programString, "!!VP1.1", 7) == 0) {
|
|
target = GL_VERTEX_PROGRAM_NV;
|
|
parseState.pos = programString + 7;
|
|
parseState.isStateProgram = GL_FALSE;
|
|
parseState.isVersion1_1 = GL_TRUE;
|
|
}
|
|
else if (strncmp((const char *) programString, "!!VSP1.0", 8) == 0) {
|
|
target = GL_VERTEX_STATE_PROGRAM_NV;
|
|
parseState.pos = programString + 8;
|
|
parseState.isStateProgram = GL_TRUE;
|
|
}
|
|
else {
|
|
/* invalid header */
|
|
ctx->Program.ErrorPos = 0;
|
|
_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)");
|
|
return;
|
|
}
|
|
|
|
|
|
if (Parse_Program(&parseState, instBuffer)) {
|
|
gl_state_index state_tokens[STATE_LENGTH] = {0, 0, 0, 0, 0};
|
|
int i;
|
|
|
|
/* successful parse! */
|
|
|
|
if (parseState.isStateProgram) {
|
|
if (!parseState.anyProgRegsWritten) {
|
|
_mesa_error(ctx, GL_INVALID_OPERATION,
|
|
"glLoadProgramNV(c[#] not written)");
|
|
return;
|
|
}
|
|
}
|
|
else {
|
|
if (!parseState.isPositionInvariant &&
|
|
!(parseState.outputsWritten & (1 << VERT_RESULT_HPOS))) {
|
|
/* bit 1 = HPOS register */
|
|
_mesa_error(ctx, GL_INVALID_OPERATION,
|
|
"glLoadProgramNV(HPOS not written)");
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* copy the compiled instructions */
|
|
assert(parseState.numInst <= MAX_NV_VERTEX_PROGRAM_INSTRUCTIONS);
|
|
newInst = _mesa_alloc_instructions(parseState.numInst);
|
|
if (!newInst) {
|
|
_mesa_error(ctx, GL_OUT_OF_MEMORY, "glLoadProgramNV");
|
|
free(programString);
|
|
return; /* out of memory */
|
|
}
|
|
_mesa_copy_instructions(newInst, instBuffer, parseState.numInst);
|
|
|
|
/* 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) {
|
|
free(program->Base.Instructions);
|
|
}
|
|
program->Base.Instructions = newInst;
|
|
program->Base.InputsRead = parseState.inputsRead;
|
|
if (parseState.isPositionInvariant)
|
|
program->Base.InputsRead |= VERT_BIT_POS;
|
|
program->Base.NumInstructions = parseState.numInst;
|
|
program->Base.OutputsWritten = parseState.outputsWritten;
|
|
program->IsPositionInvariant = parseState.isPositionInvariant;
|
|
program->IsNVProgram = GL_TRUE;
|
|
|
|
#ifdef DEBUG_foo
|
|
printf("--- glLoadProgramNV result ---\n");
|
|
_mesa_fprint_program_opt(stdout, &program->Base, PROG_PRINT_NV, 0);
|
|
printf("------------------------------\n");
|
|
#endif
|
|
|
|
if (program->Base.Parameters)
|
|
_mesa_free_parameter_list(program->Base.Parameters);
|
|
|
|
program->Base.Parameters = _mesa_new_parameter_list ();
|
|
program->Base.NumParameters = 0;
|
|
|
|
state_tokens[0] = STATE_VERTEX_PROGRAM;
|
|
state_tokens[1] = STATE_ENV;
|
|
/* Add refs to all of the potential params, in order. If we want to not
|
|
* upload everything, _mesa_layout_parameters is the answer.
|
|
*/
|
|
for (i = 0; i < MAX_NV_VERTEX_PROGRAM_PARAMS; i++) {
|
|
GLint index;
|
|
state_tokens[2] = i;
|
|
index = _mesa_add_state_reference(program->Base.Parameters,
|
|
state_tokens);
|
|
assert(index == i);
|
|
}
|
|
program->Base.NumParameters = program->Base.Parameters->NumParameters;
|
|
|
|
_mesa_setup_nv_temporary_count(ctx, &program->Base);
|
|
_mesa_emit_nv_temp_initialization(ctx, &program->Base);
|
|
}
|
|
else {
|
|
/* Error! */
|
|
_mesa_error(ctx, GL_INVALID_OPERATION, "glLoadProgramNV");
|
|
/* NOTE: _mesa_set_program_error would have been called already */
|
|
/* GL_NV_vertex_program isn't supposed to set the error string
|
|
* so we reset it here.
|
|
*/
|
|
_mesa_set_program_error(ctx, ctx->Program.ErrorPos, NULL);
|
|
}
|
|
}
|
|
|
|
|
|
const char *
|
|
_mesa_nv_vertex_input_register_name(GLuint i)
|
|
{
|
|
ASSERT(i < MAX_NV_VERTEX_PROGRAM_INPUTS);
|
|
return InputRegisters[i];
|
|
}
|
|
|
|
|
|
const char *
|
|
_mesa_nv_vertex_output_register_name(GLuint i)
|
|
{
|
|
ASSERT(i < MAX_NV_VERTEX_PROGRAM_OUTPUTS);
|
|
return OutputRegisters[i];
|
|
}
|
|
|