mirror of
https://port.numenaute.org/aleajactaest/khanat-opennel-code.git
synced 2024-12-23 01:18:46 +00:00
1143 lines
29 KiB
C++
1143 lines
29 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"
|
||
|
|
||
|
|
||
|
|
||
|
//=====================================
|
||
|
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 ouput 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 (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 ouput 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(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 ouput
|
||
|
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 (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 != '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";
|
||
|
}
|
||
|
|
||
|
|