2012-05-29 13:31:11 +00:00
// 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"
2015-02-14 13:25:50 +00:00
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
} ;
2012-05-29 13:31:11 +00:00
//=====================================
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 ' - ' :
2013-02-08 12:17:44 +00:00
errorOutput = " Negation not allowed on output register. " ;
2012-05-29 13:31:11 +00:00
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 ;
}
2015-02-14 13:25:50 +00:00
switch ( ( TArguments ) strValue )
2012-05-29 13:31:11 +00:00
{
2015-02-14 13:25:50 +00:00
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 ;
2012-05-29 13:31:11 +00:00
// texture argument
2015-02-14 13:25:50 +00:00
case TEX0 :
case TEX1 :
case TEX2 :
case TEX3 :
case TEX4 :
case TEX5 :
case TEX6 :
case TEX7 :
2012-05-29 13:31:11 +00:00
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 ! = ' [ ' )
{
2013-02-08 12:17:44 +00:00
errorOutput = " '[' expected when parsing an output register. " ;
2012-05-29 13:31:11 +00:00
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
2015-02-14 13:25:50 +00:00
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 ;
2012-05-29 13:31:11 +00:00
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 ( ) ;
2013-02-08 12:17:44 +00:00
// parse output
2012-05-29 13:31:11 +00:00
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 ) ' ' ;
}
2015-02-14 13:25:50 +00:00
switch ( ( TArguments ) instrStr )
2012-05-29 13:31:11 +00:00
{
2015-02-14 13:25:50 +00:00
case ARL :
2012-05-29 13:31:11 +00:00
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 ;
2015-02-14 13:25:50 +00:00
case RSQ :
2012-05-29 13:31:11 +00:00
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 ;
2015-02-14 13:25:50 +00:00
case EXP :
case EXPP :
2012-05-29 13:31:11 +00:00
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 ;
2015-02-14 13:25:50 +00:00
case LOG :
2012-05-29 13:31:11 +00:00
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 ;
2015-02-14 13:25:50 +00:00
case RCP :
2012-05-29 13:31:11 +00:00
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 ;
/////////////////
2015-02-14 13:25:50 +00:00
case MOV :
2012-05-29 13:31:11 +00:00
instr . Opcode = CVPInstruction : : MOV ;
if ( ! parseOp2 ( instr , errorOutput ) ) return false ;
break ;
2015-02-14 13:25:50 +00:00
case LIT :
2012-05-29 13:31:11 +00:00
instr . Opcode = CVPInstruction : : LIT ;
if ( ! parseOp2 ( instr , errorOutput ) ) return false ;
break ;
/////////////////
2015-02-14 13:25:50 +00:00
case MAD :
2012-05-29 13:31:11 +00:00
instr . Opcode = CVPInstruction : : MAD ;
if ( ! parseOp4 ( instr , errorOutput ) ) return false ;
break ;
/////////////////
2015-02-14 13:25:50 +00:00
case ADD :
2012-05-29 13:31:11 +00:00
instr . Opcode = CVPInstruction : : ADD ;
if ( ! parseOp3 ( instr , errorOutput ) ) return false ;
break ;
/////////////////
2015-02-14 13:25:50 +00:00
case MUL :
2012-05-29 13:31:11 +00:00
instr . Opcode = CVPInstruction : : MUL ;
if ( ! parseOp3 ( instr , errorOutput ) ) return false ;
break ;
2015-02-14 13:25:50 +00:00
case DP3 :
2012-05-29 13:31:11 +00:00
instr . Opcode = CVPInstruction : : DP3 ;
if ( ! parseOp3 ( instr , errorOutput ) ) return false ;
break ;
2015-02-14 13:25:50 +00:00
case DP4 :
2012-05-29 13:31:11 +00:00
instr . Opcode = CVPInstruction : : DP4 ;
if ( ! parseOp3 ( instr , errorOutput ) ) return false ;
break ;
2015-02-14 13:25:50 +00:00
case DST :
2012-05-29 13:31:11 +00:00
instr . Opcode = CVPInstruction : : DST ;
if ( ! parseOp3 ( instr , errorOutput ) ) return false ;
break ;
2015-02-14 13:25:50 +00:00
case MIN :
2012-05-29 13:31:11 +00:00
instr . Opcode = CVPInstruction : : MIN ;
if ( ! parseOp3 ( instr , errorOutput ) ) return false ;
break ;
2015-02-14 13:25:50 +00:00
case MAX :
2012-05-29 13:31:11 +00:00
instr . Opcode = CVPInstruction : : MAX ;
if ( ! parseOp3 ( instr , errorOutput ) ) return false ;
break ;
2015-02-14 13:25:50 +00:00
case SLT :
2012-05-29 13:31:11 +00:00
instr . Opcode = CVPInstruction : : SLT ;
if ( ! parseOp3 ( instr , errorOutput ) ) return false ;
break ;
2015-02-14 13:25:50 +00:00
case SGE :
2012-05-29 13:31:11 +00:00
instr . Opcode = CVPInstruction : : SGE ;
if ( ! parseOp3 ( instr , errorOutput ) ) return false ;
break ;
/////////////////
2015-02-14 13:25:50 +00:00
case END :
2012-05-29 13:31:11 +00:00
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
2013-10-06 18:06:53 +00:00
if ( instrStr ! = NELID ( " ARL " ) )
2012-05-29 13:31:11 +00:00
{
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 " ;
}