khanat-opennel-code/code/nel/src/3d/vertex_program_parse.cpp

1221 lines
30 KiB
C++

// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "std3d.h"
#include "nel/3d/vertex_program_parse.h"
enum TArguments
{
#ifdef NL_LITTLE_ENDIAN
ADD = 1094992928,
ARL = 1095912480,
BFC0 = 1111900976,
BFC1 = 1111900977,
COL0 = 1129270320,
COL1 = 1129270321,
DP3 = 1146106656,
DP4 = 1146106912,
DST = 1146311712,
END = 1162757152,
EXP = 1163415584,
EXPP = 1163415632,
FOGC = 1179600707,
HPOS = 1213222739,
LIT = 1279874080,
LOG = 1280263968,
MAD = 1296122912,
MAX = 1296128032,
MIN = 1296649760,
MOV = 1297045024,
MUL = 1297435680,
NRML = 1314016588,
OPOS = 1330663251,
PSIZ = 1347635546,
RCP = 1380143136,
RSQ = 1381191968,
SGE = 1397179680,
SLT = 1397511200,
TEX0 = 1413830704,
TEX1 = 1413830705,
TEX2 = 1413830706,
TEX3 = 1413830707,
TEX4 = 1413830708,
TEX5 = 1413830709,
TEX6 = 1413830710,
TEX7 = 1413830711,
WGHT = 1464289364,
#else
ADD = 541344833,
ARL = 541872705,
BFC0 = 809715266,
BFC1 = 826492482,
COL0 = 810307395,
COL1 = 827084611,
DP3 = 540233796,
DP4 = 540299332,
DST = 542397252,
END = 541347397,
EXP = 542136389,
EXPP = 1347442757,
FOGC = 1128746822,
HPOS = 1397706824,
LIT = 542394700,
LOG = 541544268,
MAD = 541344077,
MAX = 542654797,
MIN = 542001485,
MOV = 542527309,
MUL = 541873485,
NRML = 1280135758,
OPOS = 1397706831,
PSIZ = 1514754896,
RCP = 542131026,
RSQ = 542200658,
SGE = 541411155,
SLT = 542395475,
TEX0 = 811091284,
TEX1 = 827868500,
TEX2 = 844645716,
TEX3 = 861422932,
TEX4 = 878200148,
TEX5 = 894977364,
TEX6 = 911754580,
TEX7 = 928531796,
WGHT = 1414022999,
#endif
};
//=====================================
bool CVPParser::parseWriteMask(uint &mask, std::string &errorOutput)
{
// parse output mask
if (*_CurrChar != '.')
{
// no output masks
mask = 0xf; //output 4 coordinates
return true;
}
else
{
++ _CurrChar;
mask = 0;
for(uint k = 0; k < 4; ++k)
{
uint maskIndex;
switch(*_CurrChar)
{
case 'x': maskIndex = 0; break;
case 'y': maskIndex = 1; break;
case 'z': maskIndex = 2; break;
case 'w': maskIndex = 3; break;
default:
if (k >= 1) return true;
else
{
errorOutput = "Can't parse output mask.";
return false;
}
break;
}
++_CurrChar;
if (mask & (1 << maskIndex))
{
errorOutput = "Duplicated output mask component.";
return false;
}
mask |= 1 << maskIndex;
}
return true;
}
}
//=====================================
/** Skip tabulation and space in a source code
*/
void CVPParser::skipSpacesAndComments()
{
bool stop = false;
do
{
switch(*_CurrChar)
{
case '\t':
case '\r':
case ' ' :
++_CurrChar;
break;
//
case '\n':
++_CurrChar;
++_LineIndex;
_LineStart = _CurrChar;
break;
case '#': // comment go till end of line
while (*_CurrChar != '\n' && *_CurrChar != '\0') ++_CurrChar;
skipSpacesAndComments();
break;
default:
stop = true;
break;
}
}
while (!stop);
}
//=================================================================================================
uint CVPInstruction::getNumUsedSrc() const
{
switch(Opcode)
{
case CVPInstruction::ARL:
case CVPInstruction::RSQ:
case CVPInstruction::EXPP:
case CVPInstruction::LOG:
case CVPInstruction::RCP:
case CVPInstruction::MOV:
case CVPInstruction::LIT:
return 1;
//
case CVPInstruction::MAD:
return 3;
//
case CVPInstruction::MUL:
case CVPInstruction::ADD:
case CVPInstruction::DP3:
case CVPInstruction::DP4:
case CVPInstruction::DST:
case CVPInstruction::MIN:
case CVPInstruction::MAX:
case CVPInstruction::SLT:
case CVPInstruction::SGE:
return 2;
//
default:
nlstop;
}
return 0;
}
//=================================================================================================
bool CVPParser::parseOperand(CVPOperand &operand, bool outputOperand, std::string &errorOutput)
{
skipSpacesAndComments();
bool result;
if (outputOperand)
{
operand.Negate = false;
switch(*_CurrChar)
{
case 'o': result = parseOutputRegister(operand, errorOutput); break;
case 'R':
result = parseVariableRegister(operand, errorOutput);
break;
case 'A': result = parseAddressRegister(operand, errorOutput); break;
case '-':
errorOutput = "Negation not allowed on output register.";
return false;
default:
errorOutput = "Output, Address, or Temporary register expected as an output operand.";
return false;
}
if (!result) return false;
// parse the write mask
return parseWriteMask(operand.WriteMask, errorOutput);
}
else
{
operand.Negate = false;
switch(*_CurrChar)
{
case 'v': result = parseInputRegister(operand, errorOutput); break;
case 'R': result = parseVariableRegister(operand, errorOutput); break;
case 'c': result = parseConstantRegister(operand, errorOutput); break;
case 'a': result = parseAddressRegister(operand, errorOutput); break;
case '-':
{
operand.Negate = true;
// negation
++ _CurrChar;
skipSpacesAndComments();
switch(*_CurrChar)
{
case 'v': result = parseInputRegister(operand, errorOutput); break;
case 'R': result = parseVariableRegister(operand, errorOutput); break;
case 'c': result = parseConstantRegister(operand, errorOutput); break;
default:
errorOutput = "Negation must be followed by an input register, a variable register, or a constant.";
return false;
break;
}
}
break;
default:
errorOutput = "Syntax error.";
return false;
break;
}
if (!result) return false;
if (operand.Type != CVPOperand::AddressRegister)
{
if (!parseSwizzle(operand.Swizzle, errorOutput)) return false;
if (operand.Type == CVPOperand::Variable)
{
for(uint k = 0; k < 4; ++k)
{
if (!(_RegisterMask[operand.Value.VariableValue] & (1 << operand.Swizzle.Comp[k])))
{
errorOutput = "Can't read a register component before writing to it.";
return false;
}
}
}
}
return true;
}
}
//=================================================================================================
bool CVPParser::parseInputRegister(CVPOperand &operand, std::string &errorOutput)
{
++_CurrChar;
operand.Type = CVPOperand::InputRegister;
if (*_CurrChar != '[')
{
errorOutput = "'[' expected when parsing an input register.";
return false;
}
++_CurrChar;
skipSpacesAndComments();
if (isdigit(*_CurrChar))
{
// The input register is expressed as an index
uint index = *_CurrChar - '0';
++_CurrChar;
if (isdigit(*_CurrChar))
{
index = 10 * index + (*_CurrChar - '0');
++_CurrChar;
}
if (index > 15)
{
errorOutput = "Invalid index for input register, must be in [0, 15].";
return false;
}
operand.Value.InputRegisterValue = (CVPOperand::EInputRegister) index;
}
else
{
// The input register is expressed as a string
uint32 strValue = 0;
// read the 4 letters
for(uint k = 0; k < 4; ++k)
{
if (!isalnum(*_CurrChar))
{
errorOutput = "Can't parse index for input register.";
return false;
}
strValue |= ((uint32) *_CurrChar) << (8 * (3 - k));
++_CurrChar;
}
switch ((TArguments)strValue)
{
case OPOS: operand.Value.InputRegisterValue = CVPOperand::IPosition; break;
case WGHT: operand.Value.InputRegisterValue = CVPOperand::IWeight; break;
case NRML: operand.Value.InputRegisterValue = CVPOperand::INormal; break;
case COL0: operand.Value.InputRegisterValue = CVPOperand::IPrimaryColor; break;
case COL1: operand.Value.InputRegisterValue = CVPOperand::ISecondaryColor; break;
case FOGC: operand.Value.InputRegisterValue = CVPOperand::IFogCoord; break;
// texture argument
case TEX0:
case TEX1:
case TEX2:
case TEX3:
case TEX4:
case TEX5:
case TEX6:
case TEX7:
operand.Value.InputRegisterValue = (CVPOperand::EInputRegister) (((CVPOperand::ITex0 + strValue) & 0xff) - '0');
break;
default:
errorOutput = "Can't parse index for input register.";
return false;
}
}
skipSpacesAndComments();
if (*_CurrChar != ']')
{
errorOutput = "']' expected when parsing an input register.";
return false;
}
++ _CurrChar;
return true;
}
//=================================================================================================
static inline bool letterToSwizzleComp(char letter, CVPSwizzle::EComp &comp)
{
switch (letter)
{
case 'x': comp = CVPSwizzle::X; return true;
case 'y': comp = CVPSwizzle::Y; return true;
case 'z': comp = CVPSwizzle::Z; return true;
case 'w': comp = CVPSwizzle::W; return true;
}
return false;
}
//=================================================================================================
bool CVPParser::parseSwizzle(CVPSwizzle &swizzle,std::string &errorOutput)
{
if (*_CurrChar != '.')
{
// no swizzle
swizzle.Comp[0] = CVPSwizzle::X;
swizzle.Comp[1] = CVPSwizzle::Y;
swizzle.Comp[2] = CVPSwizzle::Z;
swizzle.Comp[3] = CVPSwizzle::W;
return true;
}
++_CurrChar;
// 4 letters case
for(uint k = 0; k < 4; ++k)
{
if (!isalpha(*_CurrChar))
{
if (k == 1) // 1 letter case
{
switch(*_CurrChar)
{
case ',':
case ';':
case ' ':
case '\t':
case '\r':
case '\n':
case '#':
swizzle.Comp[1] = swizzle.Comp[2] = swizzle.Comp[3] = swizzle.Comp[0];
return true;
break;
default:
errorOutput = "Can't parse swizzle.";
}
}
else
{
errorOutput = "Invalid swizzle value.";
return false;
}
}
if (!letterToSwizzleComp(*_CurrChar, swizzle.Comp[k]))
{
errorOutput = "Invalid swizzle value.";
return false;
}
++ _CurrChar;
}
return true;
}
//=================================================================================================
bool CVPParser::parseOutputRegister(CVPOperand &operand, std::string &errorOutput)
{
++_CurrChar;
operand.Type = CVPOperand::OutputRegister;
if (*_CurrChar != '[')
{
errorOutput = "'[' expected when parsing an output register.";
return false;
}
++_CurrChar;
skipSpacesAndComments();
// The input register is expressed as a string
uint32 strValue = 0;
// read the 4 letters
for(uint k = 0; k < 4; ++k)
{
if (!isalnum(*_CurrChar))
{
errorOutput = "Can't parse index for output register.";
return false;
}
strValue |= ((uint32) *_CurrChar) << (8 * (3 - k));
++_CurrChar;
}
// convert to enum
switch((TArguments)strValue)
{
case HPOS: operand.Value.OutputRegisterValue = CVPOperand::OHPosition; break;
case COL0: operand.Value.OutputRegisterValue = CVPOperand::OPrimaryColor; break;
case COL1: operand.Value.OutputRegisterValue = CVPOperand::OSecondaryColor; break;
case BFC0: operand.Value.OutputRegisterValue = CVPOperand::OBackFacePrimaryColor; break;
case BFC1: operand.Value.OutputRegisterValue = CVPOperand::OBackFaceSecondaryColor; break;
case FOGC: operand.Value.OutputRegisterValue = CVPOperand::OFogCoord; break;
case PSIZ: operand.Value.OutputRegisterValue = CVPOperand::OPointSize; break;
case TEX0: operand.Value.OutputRegisterValue = CVPOperand::OTex0; break;
case TEX1: operand.Value.OutputRegisterValue = CVPOperand::OTex1; break;
case TEX2: operand.Value.OutputRegisterValue = CVPOperand::OTex2; break;
case TEX3: operand.Value.OutputRegisterValue = CVPOperand::OTex3; break;
case TEX4: operand.Value.OutputRegisterValue = CVPOperand::OTex4; break;
case TEX5: operand.Value.OutputRegisterValue = CVPOperand::OTex5; break;
case TEX6: operand.Value.OutputRegisterValue = CVPOperand::OTex6; break;
case TEX7: operand.Value.OutputRegisterValue = CVPOperand::OTex7; break;
default:
errorOutput = "Can't read index for output register.";
return false;
break;
}
skipSpacesAndComments();
if (*_CurrChar != ']')
{
errorOutput = "']' expected when parsing an output register.";
return false;
}
++_CurrChar;
return true;
}
//=================================================================================================
static inline const char *parseUInt(const char *src, uint &dest)
{
uint index = 0;
while (isdigit(*src))
{
index = 10 * index + *src - '0';
++ src;
}
dest = index;
return src;
}
//=================================================================================================
bool CVPParser::parseConstantRegister(CVPOperand &operand, std::string &errorOutput)
{
++_CurrChar;
operand.Type = CVPOperand::Constant;
if (*_CurrChar != '[')
{
errorOutput = "'[' expected when parsing a constant register.";
return false;
}
++_CurrChar;
skipSpacesAndComments();
sint &index = operand.Value.ConstantValue;
if (isdigit(*_CurrChar))
{
// immediat case : c[0] to c[95]
uint uIndex;
_CurrChar = parseUInt(_CurrChar, uIndex);
if (uIndex > 95)
{
errorOutput = "Constant register index must range from 0 to 95.";
return false;
}
index = (sint) uIndex;
operand.Indexed = false;
}
else if (*_CurrChar == 'A')
{
// indexed case : c[A0.x - 64] to c[A0.x + 63]
operand.Indexed = true;
index = 0;
if (_CurrChar[1] == '0'
&& _CurrChar[2] == '.'
&& _CurrChar[3] == 'x')
{
_CurrChar += 4;
skipSpacesAndComments();
if (*_CurrChar == '+')
{
++ _CurrChar;
skipSpacesAndComments();
if (isdigit(*_CurrChar))
{
uint uIndex;
_CurrChar = parseUInt(_CurrChar, uIndex);
if (uIndex > 63)
{
errorOutput = "Constant register index must range from -64 to +63.";
return false;
}
index = (sint) uIndex;
}
else
{
errorOutput = "Can't parse offset for constant register.";
return false;
}
}
else
if (*_CurrChar == '-')
{
++ _CurrChar;
skipSpacesAndComments();
if (isdigit(*_CurrChar))
{
uint uIndex;
_CurrChar = parseUInt(_CurrChar, uIndex);
if (uIndex > 64)
{
errorOutput = "Constant register index must range from -64 to +63.";
return false;
}
index = - (sint) uIndex;
}
else
{
errorOutput = "Can't parse offset for constant register.";
return false;
}
}
}
else
{
errorOutput = "Can't parse constant register index.";
return false;
}
}
skipSpacesAndComments();
if (*_CurrChar != ']')
{
errorOutput = "']' expected when parsing an input register.";
return false;
}
++_CurrChar;
return true;
}
//=================================================================================================
bool CVPParser::parseVariableRegister(CVPOperand &operand, std::string &errorOutput)
{
++_CurrChar;
operand.Type = CVPOperand::Variable;
if (!isdigit(*_CurrChar))
{
errorOutput = "Can't parse variable register.";
return false;
}
uint &index = operand.Value.VariableValue;
_CurrChar = parseUInt(_CurrChar, index);
if (index > 11)
{
errorOutput = "Variable register index must range from 0 to 11.";
return false;
}
return true;
}
//=================================================================================================
bool CVPParser::parseAddressRegister(CVPOperand &operand, std::string &errorOutput)
{
++_CurrChar;
operand.Type = CVPOperand::AddressRegister;
if (_CurrChar[0] != '0' || _CurrChar[1] != '.' || _CurrChar[2] != 'x')
{
errorOutput = "Can't parse address register.";
return false;
}
_CurrChar += 3;
return true;
}
//=================================================================================================
bool CVPParser::parseOp2(CVPInstruction &instr,std::string &errorOutput)
{
skipSpacesAndComments();
// parse output
if (!parseOperand(instr.Dest, true, errorOutput)) return false;
// Can't write in input or consant register
if (instr.Dest.Type == CVPOperand::Constant || instr.Dest.Type == CVPOperand::InputRegister)
{
errorOutput = "Can't write to a constant or input register";
return false;
}
//
skipSpacesAndComments();
if (*_CurrChar != ',')
{
errorOutput = "',' expected.";
return false;
}
++_CurrChar;
skipSpacesAndComments();
// parse src1
if (!parseOperand(instr.Src1, false, errorOutput)) return false;
if (instr.Src1.Type == CVPOperand::AddressRegister
|| instr.Src1.Type == CVPOperand::OutputRegister)
{
errorOutput = "Src1 must be constant, variable, or input register.";
return false;
}
return true;
}
//=================================================================================================
bool CVPParser::parseOp3(CVPInstruction &instr, std::string &errorOutput)
{
if (!parseOp2(instr, errorOutput)) return false;
skipSpacesAndComments();
if (*_CurrChar != ',')
{
errorOutput = "',' expected.";
return false;
}
++_CurrChar;
skipSpacesAndComments();
// parse src2
if (!parseOperand(instr.Src2, false, errorOutput)) return false;
if (instr.Src2.Type == CVPOperand::AddressRegister
|| instr.Src2.Type == CVPOperand::OutputRegister)
{
errorOutput = "Src2 must be constant, variable, or input register.";
return false;
}
// make sure we do not have 2 =/= contant register as src (or in put register)
// 2 constant registers ?
if (instr.Src1.Type == CVPOperand::Constant
&& instr.Src2.Type == CVPOperand::Constant)
{
// the index must be the same
if (!
(
instr.Src1.Indexed == instr.Src2.Indexed
&& instr.Src1.Value.ConstantValue == instr.Src2.Value.ConstantValue
)
)
{
errorOutput = "Can't read 2 different constant registers in a single instruction.";
return false;
}
}
// 2 input registers ?
if (instr.Src1.Type == CVPOperand::InputRegister
&& instr.Src2.Type == CVPOperand::InputRegister)
{
// the index must be the same
if (instr.Src1.Value.InputRegisterValue != instr.Src2.Value.InputRegisterValue)
{
errorOutput = "Can't read 2 different input registers in a single instruction.";
return false;
}
}
return true;
}
//=================================================================================================
bool CVPParser::parseOp4(CVPInstruction &instr, std::string &errorOutput)
{
if (!parseOp3(instr, errorOutput)) return false;
// parse src 3
skipSpacesAndComments();
if (*_CurrChar != ',')
{
errorOutput = "',' expected.";
return false;
}
++_CurrChar;
skipSpacesAndComments();
// parse src4
if (!parseOperand(instr.Src3, false, errorOutput)) return false;
if (instr.Src3.Type == CVPOperand::AddressRegister
|| instr.Src3.Type == CVPOperand::OutputRegister)
{
errorOutput = "Src3 must be constant, variable, or input register.";
return false;
}
///////////////////////////////////////////////////
// check for different contant / input registers //
///////////////////////////////////////////////////
// Duplicated constant register
if (instr.Src3.Type == CVPOperand::Constant)
{
if (instr.Src1.Type == CVPOperand::Constant)
{
if (!
(
instr.Src1.Indexed == instr.Src3.Indexed
&& instr.Src1.Value.ConstantValue == instr.Src3.Value.ConstantValue
)
)
{
errorOutput = "Can't read 2 different constant registers in a single instruction.";
return false;
}
}
if (instr.Src2.Type == CVPOperand::Constant)
{
if (!
(
instr.Src2.Indexed == instr.Src3.Indexed
&& instr.Src2.Value.ConstantValue == instr.Src3.Value.ConstantValue
)
)
{
errorOutput = "Can't read 2 different constant registers in a single instruction.";
return false;
}
}
}
// Duplicated input register
if (instr.Src3.Type == CVPOperand::InputRegister)
{
if (instr.Src1.Type == CVPOperand::InputRegister)
{
if (instr.Src1.Value.InputRegisterValue != instr.Src3.Value.InputRegisterValue)
{
errorOutput = "Can't read 2 different input registers in a single instruction.";
return false;
}
}
if (instr.Src2.Type == CVPOperand::InputRegister)
{
if (instr.Src2.Value.InputRegisterValue != instr.Src3.Value.InputRegisterValue)
{
errorOutput = "Can't read 2 different input registers in a single instruction.";
return false;
}
}
}
return true;
}
//=================================================================================================
bool CVPParser::parseInstruction(CVPInstruction &instr, std::string &errorOutput, bool &endEncountered)
{
skipSpacesAndComments();
endEncountered = false;
uint32 instrStr = 0;
uint k;
for(k = 0; k < 4; ++k)
{
if (!isalnum(*_CurrChar))
{
if (k < 3) // at least 3 letter in an instruction
{
errorOutput = "Syntax error : can't read opcode.";
return false;
}
else break;
}
instrStr |= ((uint) *_CurrChar) << (8 * (3 - k));
++ _CurrChar;
}
if (k != 4)
{
instrStr |= (uint32) ' ';
}
switch ((TArguments)instrStr)
{
case ARL:
instr.Opcode = CVPInstruction::ARL;
if (!parseOp2(instr, errorOutput)) return false;
if (!instr.Src1.Swizzle.isScalar())
{
errorOutput = "ARL need a scalar src value.";
return false;
}
break;
case RSQ:
instr.Opcode = CVPInstruction::RSQ;
if (!parseOp2(instr, errorOutput)) return false;
if (!instr.Src1.Swizzle.isScalar())
{
errorOutput = "RSQ need a scalar src value.";
return false;
}
break;
case EXP:
case EXPP:
instr.Opcode = CVPInstruction::EXPP;
if (!parseOp2(instr, errorOutput)) return false;
if (!instr.Src1.Swizzle.isScalar())
{
errorOutput = "EXP need a scalar src value.";
return false;
}
/*
if (instr.Src1.Swizzle.Comp[0] != CVPSwizzle.W)
{
errorOutput = "EXPP input scalar must be w";
return false;
}*/
break;
case LOG:
instr.Opcode = CVPInstruction::LOG;
if (!parseOp2(instr, errorOutput)) return false;
if (!instr.Src1.Swizzle.isScalar())
{
errorOutput = "LOG need a scalar src value.";
return false;
}
/*
if (instr.Src1.Swizzle.Comp[0] != CVPSwizzle.W)
{
errorOutput = "LOG input scalar must be w";
return false;
}
*/
break;
case RCP:
instr.Opcode = CVPInstruction::RCP;
if (!parseOp2(instr, errorOutput)) return false;
if (!instr.Src1.Swizzle.isScalar())
{
errorOutput = "RCP need a scalar src value.";
return false;
}
break;
/////////////////
case MOV:
instr.Opcode = CVPInstruction::MOV;
if (!parseOp2(instr, errorOutput)) return false;
break;
case LIT:
instr.Opcode = CVPInstruction::LIT;
if (!parseOp2(instr, errorOutput)) return false;
break;
/////////////////
case MAD:
instr.Opcode = CVPInstruction::MAD;
if (!parseOp4(instr, errorOutput)) return false;
break;
/////////////////
case ADD:
instr.Opcode = CVPInstruction::ADD;
if (!parseOp3(instr, errorOutput)) return false;
break;
/////////////////
case MUL:
instr.Opcode = CVPInstruction::MUL;
if (!parseOp3(instr, errorOutput)) return false;
break;
case DP3:
instr.Opcode = CVPInstruction::DP3;
if (!parseOp3(instr, errorOutput)) return false;
break;
case DP4:
instr.Opcode = CVPInstruction::DP4;
if (!parseOp3(instr, errorOutput)) return false;
break;
case DST:
instr.Opcode = CVPInstruction::DST;
if (!parseOp3(instr, errorOutput)) return false;
break;
case MIN:
instr.Opcode = CVPInstruction::MIN;
if (!parseOp3(instr, errorOutput)) return false;
break;
case MAX:
instr.Opcode = CVPInstruction::MAX;
if (!parseOp3(instr, errorOutput)) return false;
break;
case SLT:
instr.Opcode = CVPInstruction::SLT;
if (!parseOp3(instr, errorOutput)) return false;
break;
case SGE:
instr.Opcode = CVPInstruction::SGE;
if (!parseOp3(instr, errorOutput)) return false;
break;
/////////////////
case END:
endEncountered = true;
return true;
break;
default:
errorOutput = "Syntax error : unknow opcode.";
return false;
break;
}
if (instr.Dest.Type == CVPOperand::Variable)
{
_RegisterMask[instr.Dest.Value.VariableValue] |= instr.Dest.WriteMask;
}
// it is not allowed to write to an adress register except for ARL
if (instrStr != NELID("ARL "))
{
if (instr.Dest.Type == CVPOperand::AddressRegister)
{
errorOutput = "Can't write to address register.";
return false;
}
}
// parse semi-colon
skipSpacesAndComments();
//
if (*_CurrChar != ';')
{
errorOutput = "';' expected.";
return false;
}
++_CurrChar;
return true;
}
//=================================================================================================
bool CVPParser::isInputUsed(const TProgram &prg, CVPOperand::EInputRegister input)
{
for(uint k = 0; k < prg.size(); ++k)
{
uint numSrc = prg[k].getNumUsedSrc();
for(uint l = 0; l < numSrc; ++l)
{
const CVPOperand &src = prg[k].getSrc(l);
if (src.Type == CVPOperand::InputRegister && src.Value.InputRegisterValue == input) return true;
}
}
return false;
}
//=================================================================================================
static std::string getStringUntilCR(const char *src)
{
nlassert(src);
std::string result;
while (*src != '\n' && *src != '\r' && *src != '\0')
{
result += *src;
++src;
}
return result;
}
//=================================================================================================
bool CVPParser::parse(const char *src, CVPParser::TProgram &result, std::string &errorOutput)
{
if (!src) return false;
//
std::fill(_RegisterMask, _RegisterMask + 96, 0);
//
_CurrChar = src;
_LineStart = src;
_LineIndex = 1;
//
//skipSpacesAndComments(); // in fact space are not allowed at the start of the vertex program
// parse version
if ( _CurrChar[0] != '!'
|| _CurrChar[1] != '!'
|| _CurrChar[2] != 'V'
|| _CurrChar[3] != 'P'
|| _CurrChar[4] != '1'
|| _CurrChar[5] != '.'
|| (_CurrChar[6] != '0' && _CurrChar[6] != '1'))
{
errorOutput = "Can't parse version.";
return false;
}
_CurrChar += 7;
errorOutput.clear();
// parse instructions
bool endEncoutered = false;
std::string errorMess;
for(;;)
{
CVPInstruction instr;
if (!parseInstruction(instr, errorMess, endEncoutered))
{
errorOutput = std::string("CVPParser::parse : Error encountered at line ") + NLMISC::toString(_LineIndex) + std::string(" : ") + errorMess + std::string(" Text : ") + getStringUntilCR(_LineStart);
return false;
}
if (endEncoutered) break;
result.push_back(instr);
}
return true;
}
//=================================================================================================
static const char *instrToName[] =
{
"MOV ",
"ARL ",
"MUL ",
"ADD ",
"MAD ",
"RSQ ",
"DP3 ",
"DP4 ",
"DST ",
"LIT ",
"MIN ",
"MAX ",
"SLT ",
"SGE ",
"EXPP ",
"LOG ",
"RCP "
};
//=================================================================================================
static const char *outputRegisterToName[] =
{
"HPOS",
"COL0",
"COL1",
"BFC0",
"BFC1",
"FOGC",
"PSIZ",
"TEX0",
"TEX1",
"TEX2",
"TEX3",
"TEX4",
"TEX5",
"TEX6",
"TEX7"
};
//=================================================================================================
static void dumpWriteMask(uint mask, std::string &out)
{
if (mask == 0xf)
{
out = "";
return;
}
out = ".";
if (mask & 1) out +="x";
if (mask & 2) out +="y";
if (mask & 4) out +="z";
if (mask & 8) out +="w";
}
//=================================================================================================
static void dumpSwizzle(const CVPSwizzle &swz, std::string &out)
{
if (swz.isIdentity())
{
out = "";
return;
}
out = ".";
for(uint k = 0; k < 4; ++k)
{
switch(swz.Comp[k])
{
case CVPSwizzle::X: out += "x"; break;
case CVPSwizzle::Y: out += "y"; break;
case CVPSwizzle::Z: out += "z"; break;
case CVPSwizzle::W: out += "w"; break;
default:
nlassert(0);
break;
}
if (swz.isScalar() && k == 0) break;
}
}
//=================================================================================================
static void dumpOperand(const CVPOperand &op, bool destOperand, std::string &out)
{
out = op.Negate ? " -" : " ";
switch(op.Type)
{
case CVPOperand::Variable: out += "R" + NLMISC::toString(op.Value.VariableValue); break;
case CVPOperand::Constant:
out += "c[";
if (op.Indexed)
{
out += "A0.x + ";
}
out += NLMISC::toString(op.Value.ConstantValue) + "]";
break;
case CVPOperand::InputRegister: out += "v[" + NLMISC::toString((uint) op.Value.InputRegisterValue) + "]"; break;
case CVPOperand::OutputRegister:
nlassert(op.Value.OutputRegisterValue < CVPOperand::OutputRegisterCount);
out += "o[" + std::string(outputRegisterToName[op.Value.OutputRegisterValue]) + "]";
break;
case CVPOperand::AddressRegister:
out += "A0.x";
break;
default:
break;
}
std::string suffix;
if (destOperand)
{
dumpWriteMask(op.WriteMask, suffix);
}
else
{
dumpSwizzle(op.Swizzle, suffix);
}
out += suffix;
}
//=================================================================================================
/** Dump an instruction in a string
*/
static void dumpInstr(const CVPInstruction &instr, std::string &out)
{
nlassert(instr.Opcode < CVPInstruction::OpcodeCount);
out = instrToName[instr.Opcode];
uint nbOp = instr.getNumUsedSrc();
std::string destOperand;
dumpOperand(instr.Dest, true, destOperand);
out += destOperand;
for(uint k = 0; k < nbOp; ++k)
{
out += ", ";
std::string srcOperand;
dumpOperand(instr.getSrc(k), false, srcOperand);
out += srcOperand;
}
out +="; \n";
}
//=================================================================================================
void CVPParser::dump(const TProgram &prg, std::string &dest)
{
dest = "!!VP1.0 \n";
for(uint k = 0; k < prg.size(); ++k)
{
std::string instr;
dumpInstr(prg[k], instr);
dest += instr;
}
dest +="END";
}