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"
//=====================================
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 ;
}
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 ! = ' [ ' )
{
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
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 ( ) ;
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 ) ' ' ;
}
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 " ;
}