sub WriteHelperVar
{
local( $name ) = shift;
local( $min ) = shift;
local( $max ) = shift;
local( $varname ) = "m_n" . $name;
local( $boolname ) = "m_b" . $name;
push @outputHeader, "private:\n";
push @outputHeader, "\tint $varname;\n";
push @outputHeader, "#ifdef _DEBUG\n";
push @outputHeader, "\tbool $boolname;\n";
push @outputHeader, "#endif\n";
push @outputHeader, "public:\n";
# int version of set function
push @outputHeader, "\tvoid Set" . $name . "( int i )\n";
push @outputHeader, "\t{\n";
if ( $min != $max )
{
push @outputHeader, "\t\tAssert( i >= $min && i <= $max );\n";
push @outputHeader, "\t\t$varname = i;\n";
push @outputHeader, "#ifdef _DEBUG\n";
push @outputHeader, "\t\t$boolname = true;\n";
push @outputHeader, "#endif\n";
}
push @outputHeader, "\t}\n";
# bool version of set function
push @outputHeader, "\tvoid Set" . $name . "( bool i )\n";
push @outputHeader, "\t{\n";
if ( $min != $max )
{
# push @outputHeader, "\t\tAssert( i >= $min && i <= $max );\n";
push @outputHeader, "\t\t$varname = i ? 1 : 0;\n";
push @outputHeader, "#ifdef _DEBUG\n";
push @outputHeader, "\t\t$boolname = true;\n";
push @outputHeader, "#endif\n";
}
push @outputHeader, "\t}\n";
}
sub WriteStaticBoolExpression
{
local( $prefix ) = shift;
local( $operator ) = shift;
for( $i = 0; $i < scalar( @staticDefineNames ); $i++ )
{
if( $i )
{
push @outputHeader, " $operator ";
}
local( $name ) = @staticDefineNames[$i];
local( $boolname ) = "m_b" . $name;
push @outputHeader, "$prefix$boolname";
}
push @outputHeader, ";\n";
}
sub WriteDynamicBoolExpression
{
local( $prefix ) = shift;
local( $operator ) = shift;
for( $i = 0; $i < scalar( @dynamicDefineNames ); $i++ )
{
if( $i )
{
push @outputHeader, " $operator ";
}
local( $name ) = @dynamicDefineNames[$i];
local( $boolname ) = "m_b" . $name;
push @outputHeader, "$prefix$boolname";
}
push @outputHeader, ";\n";
}
sub WriteDynamicHelperClasses
{
local( $basename ) = $fxc_filename;
$basename =~ s/\.fxc//i;
$basename =~ tr/A-Z/a-z/;
local( $classname ) = $basename . "_Dynamic_Index";
push @outputHeader, "class $classname\n";
push @outputHeader, "{\n";
for( $i = 0; $i < scalar( @dynamicDefineNames ); $i++ )
{
$name = $dynamicDefineNames[$i];
$min = $dynamicDefineMin[$i];
$max = $dynamicDefineMax[$i];
&WriteHelperVar( $name, $min, $max );
}
push @outputHeader, "public:\n";
push @outputHeader, "\t$classname()\n";
push @outputHeader, "\t{\n";
for( $i = 0; $i < scalar( @dynamicDefineNames ); $i++ )
{
$min = $dynamicDefineMin[$i];
$max = $dynamicDefineMax[$i];
local( $name ) = @dynamicDefineNames[$i];
local( $boolname ) = "m_b" . $name;
local( $varname ) = "m_n" . $name;
push @outputHeader, "#ifdef _DEBUG\n";
if ( $min != $max )
{
push @outputHeader, "\t\t$boolname = false;\n";
}
else
{
push @outputHeader, "\t\t$boolname = true;\n";
}
push @outputHeader, "#endif // _DEBUG\n";
push @outputHeader, "\t\t$varname = 0;\n";
}
push @outputHeader, "\t}\n";
push @outputHeader, "\tint GetIndex()\n";
push @outputHeader, "\t{\n";
push @outputHeader, "\t\t// Asserts to make sure that we aren't using any skipped combinations.\n";
foreach $skip (@perlskipcodeindividual)
{
$skip =~ s/\$/m_n/g;
# push @outputHeader, "\t\tAssert( !( $skip ) );\n";
}
push @outputHeader, "\t\t// Asserts to make sure that we are setting all of the combination vars.\n";
push @outputHeader, "#ifdef _DEBUG\n";
if( scalar( @dynamicDefineNames ) > 0 )
{
push @outputHeader, "\t\tbool bAllDynamicVarsDefined = ";
WriteDynamicBoolExpression( "", "&&" );
}
if( scalar( @dynamicDefineNames ) > 0 )
{
push @outputHeader, "\t\tAssert( bAllDynamicVarsDefined );\n";
}
push @outputHeader, "#endif // _DEBUG\n";
if( $spewCombos && scalar( @dynamicDefineNames ) )
{
push @outputHeader, &CreateCCodeToSpewDynamicCombo();
}
push @outputHeader, "\t\treturn ";
local( $scale ) = 1;
for( $i = 0; $i < scalar( @dynamicDefineNames ); $i++ )
{
local( $name ) = @dynamicDefineNames[$i];
local( $varname ) = "m_n" . $name;
push @outputHeader, "( $scale * $varname ) + ";
$scale *= $dynamicDefineMax[$i] - $dynamicDefineMin[$i] + 1;
}
push @outputHeader, "0;\n";
push @outputHeader, "\t}\n";
push @outputHeader, "};\n";
}
sub WriteStaticHelperClasses
{
local( $basename ) = $fxc_filename;
$basename =~ s/\.fxc//i;
$basename =~ tr/A-Z/a-z/;
local( $classname ) = $basename . "_Static_Index";
push @outputHeader, "class $classname\n";
push @outputHeader, "{\n";
for( $i = 0; $i < scalar( @staticDefineNames ); $i++ )
{
$name = $staticDefineNames[$i];
$min = $staticDefineMin[$i];
$max = $staticDefineMax[$i];
&WriteHelperVar( $name, $min, $max );
}
push @outputHeader, "public:\n";
push @outputHeader, "\t$classname()\n";
push @outputHeader, "\t{\n";
for( $i = 0; $i < scalar( @staticDefineNames ); $i++ )
{
$min = $staticDefineMin[$i];
$max = $staticDefineMax[$i];
local( $name ) = @staticDefineNames[$i];
local( $boolname ) = "m_b" . $name;
local( $varname ) = "m_n" . $name;
push @outputHeader, "#ifdef _DEBUG\n";
if ( $min != $max )
{
push @outputHeader, "\t\t$boolname = false;\n";
}
else
{
push @outputHeader, "\t\t$boolname = true;\n";
}
push @outputHeader, "#endif // _DEBUG\n";
push @outputHeader, "\t\t$varname = 0;\n";
}
push @outputHeader, "\t}\n";
push @outputHeader, "\tint GetIndex()\n";
push @outputHeader, "\t{\n";
push @outputHeader, "\t\t// Asserts to make sure that we aren't using any skipped combinations.\n";
foreach $skip (@perlskipcodeindividual)
{
$skip =~ s/\$/m_n/g;
# push @outputHeader, "\t\tAssert( !( $skip ) );\n";
}
push @outputHeader, "\t\t// Asserts to make sure that we are setting all of the combination vars.\n";
push @outputHeader, "#ifdef _DEBUG\n";
if( scalar( @staticDefineNames ) > 0 )
{
push @outputHeader, "\t\tbool bAllStaticVarsDefined = ";
WriteStaticBoolExpression( "", "&&" );
}
if( scalar( @staticDefineNames ) > 0 )
{
push @outputHeader, "\t\tAssert( bAllStaticVarsDefined );\n";
}
push @outputHeader, "#endif // _DEBUG\n";
if( $spewCombos && scalar( @staticDefineNames ) )
{
push @outputHeader, &CreateCCodeToSpewStaticCombo();
}
push @outputHeader, "\t\treturn ";
local( $scale ) = 1;
for( $i = 0; $i < scalar( @dynamicDefineNames ); $i++ )
{
$scale *= $dynamicDefineMax[$i] - $dynamicDefineMin[$i] + 1;
}
for( $i = 0; $i < scalar( @staticDefineNames ); $i++ )
{
local( $name ) = @staticDefineNames[$i];
local( $varname ) = "m_n" . $name;
push @outputHeader, "( $scale * $varname ) + ";
$scale *= $staticDefineMax[$i] - $staticDefineMin[$i] + 1;
}
push @outputHeader, "0;\n";
push @outputHeader, "\t}\n";
push @outputHeader, "};\n";
}
sub CreateFuncToSetPerlVars
{
local( $out ) = "";
$out .= "sub SetPerlVarsFunc\n";
$out .= "{\n";
$out .= " local( \$combo ) = shift;\n";
$out .= " local( \$i );\n";
local( $i );
for( $i = 0; $i < scalar( @dynamicDefineNames ); \$i++ )
{
$out .= " \$$dynamicDefineNames[$i] = \$combo % ";
$out .= ( $dynamicDefineMax[$i] - $dynamicDefineMin[$i] + 1 ) + $dynamicDefineMin[$i];
$out .= ";\n";
$out .= " \$combo = \$combo / " . ( $dynamicDefineMax[$i] - $dynamicDefineMin[$i] + 1 ) . ";\n";
}
for( $i = 0; $i < scalar( @staticDefineNames ); \$i++ )
{
$out .= " \$$staticDefineNames[$i] = \$combo % ";
$out .= ( $staticDefineMax[$i] - $staticDefineMin[$i] + 1 ) + $staticDefineMin[$i];
$out .= ";\n";
$out .= " \$combo = \$combo / " . ( $staticDefineMax[$i] - $staticDefineMin[$i] + 1 ) . ";\n";
}
$out .= "}\n";
# print $out;
eval $out;
}
sub BackToForwardSlash
{
my( $path ) = shift;
$path =~ s,\\,/,g;
return $path;
}
sub RemoveFileName
{
my( $in ) = shift;
$in = &BackToForwardSlash( $in );
$in =~ s,/[^/]*$,,;
return $in;
}
sub RemovePath
{
my( $in ) = shift;
$in = &BackToForwardSlash( $in );
$in =~ s,^(.*)/([^/]*)$,$2,;
return $in;
}
sub MakeDirHier
{
my( $in ) = shift;
# print "MakeDirHier( $in )\n";
$in = &BackToForwardSlash( $in );
my( @path );
while( $in =~ m,/, ) # while $in still has a slash
{
my( $end ) = &RemovePath( $in );
push @path, $end;
# print $in . "\n";
$in = &RemoveFileName( $in );
}
my( $i );
my( $numelems ) = scalar( @path );
my( $curpath );
for( $i = $numelems - 1; $i >= 0; $i-- )
{
$curpath .= "/" . $path[$i];
my( $dir ) = $in . $curpath;
if( !stat $dir )
{
# print "mkdir $dir\n";
mkdir $dir, 0777;
}
}
}
# These sections can be interchanged to enable profiling.
#$ShowTimers = 1;
#use Time::HiRes;
#sub SampleTime()
#{
# return Time::HiRes::time();
#}
$ShowTimers = 0;
sub SampleTime() { return 0; }
$total_start_time = SampleTime();
# NOTE: These must match the same values in macros.vsh!
$vPos = "v0";
$vBoneWeights = "v1";
$vBoneIndices = "v2";
$vNormal = "v3";
if( $g_xbox )
{
$vPosFlex = "v4";
$vNormalFlex = "v13";
}
$vColor = "v5";
$vSpecular = "v6";
$vTexCoord0 = "v7";
$vTexCoord1 = "v8";
$vTexCoord2 = "v9";
$vTexCoord3 = "v10";
$vTangentS = "v11";
$vTangentT = "v12";
$vUserData = "v14";
sub ReadInputFile
{
local( $base_filename ) = shift;
local( *INPUT );
local( @output );
# Look in the stdshaders directory, followed by the current directory.
# (This is for the SDK, since some of its files are under stdshaders).
local( $filename ) = $base_filename;
if ( !-e $filename )
{
$filename = "$g_SourceDir\\materialsystem\\stdshaders\\$base_filename";
if ( !-e $filename )
{
die "\nERROR: missing include file: $filename.\n\n";
}
}
open INPUT, "<$filename" || die;
local( $line );
local( $linenum ) = 1;
while( $line = )
{
$line =~ s/\n//g;
local( $postfix ) = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
$postfix .= "; LINEINFO($filename)($linenum)\n";
if( $line =~ m/\#include\s+\"(.*)\"/i )
{
push @output, &ReadInputFile( $1 );
}
else
{
push @output, $line . $postfix;
}
$linenum++;
}
close INPUT;
return @output;
}
sub IsPerl
{
local( $line ) = shift;
if( $line =~ m/^\s*sub.*\,/ )
{
return 0;
}
if( $line =~ m/^\#include/ ||
$line =~ m/^\#define/ ||
$line =~ m/^\#undef/ ||
$line =~ m/^\#ifdef/ ||
$line =~ m/^\#ifndef/ ||
$line =~ m/^\#else/ ||
$line =~ m/^\#endif/ ||
$line =~ m/^\#error/
)
{
return 0;
}
if( $line =~ m/^\s*if\s*\(/ ||
$line =~ m/^\s*else/ ||
$line =~ m/^\s*elsif/ ||
$line =~ m/^\s*for\s*\(/ ||
$line =~ m/^\s*\{/ ||
$line =~ m/^sub\s*/ ||
$line =~ m/^\s*\}/ ||
$line =~ m/^\s*\&/ ||
$line =~ m/^\s*\#/ ||
$line =~ m/^\s*\$/ ||
$line =~ m/^\s*print/ ||
$line =~ m/^\s*return/ ||
$line =~ m/^\s*exit/ ||
$line =~ m/^\s*die/ ||
$line =~ m/^\s*eval/ ||
$line =~ m/^\s*local/ ||
$line =~ m/^\s*my\s+/ ||
$line =~ m/^\s*@/ ||
$line =~ m/^\s*alloc\s+/ ||
$line =~ m/^\s*free\s+/
)
{
return 1;
}
return 0;
}
# translate the output into something that takes us back to the source line
# that we care about in msdev
sub TranslateErrorMessages
{
local( $origline );
while( $origline = shift )
{
if( $origline =~ m/(.*)\((\d+)\)\s*:\s*(.*)$/i )
{
local( $filename ) = $1;
local( $linenum ) = $2;
local( $error ) = $3;
local( *FILE );
open FILE, "<$filename" || die;
local( $i );
local( $line );
for( $i = 1; $i < $linenum; $i++ )
{
$line = ;
}
if( $line =~ m/LINEINFO\((.*)\)\((.*)\)/ )
{
print "$1\($2\) : $error\n";
my $num = $linenum - 1;
print "$filename\($num\) : original error location\n";
}
close FILE;
}
else
{
$origline =~ s/successful compile\!.*//gi;
if( !( $origline =~ m/^\s*$/ ) )
{
# print "WTF: $origline\n";
}
}
}
}
sub CountInstructions
{
local( $line );
local( $count ) = 0;
while( $line = shift )
{
# get rid of comments
$line =~ s/;.*//gi;
$line =~ s/\/\/.*//gi;
# skip the vs1.1 statement
$line =~ s/^\s*vs.*//gi;
# if there's any text left, it's an instruction
if( $line =~ /\S/gi )
{
$count++;
}
}
return $count;
}
%compiled = ();
sub UsesRegister
{
my $registerName = shift;
my $str = shift;
# Cache a compiled RE for each register name. This makes UsesRegister about 2.5x faster.
if ( !$compiled{$registerName} )
{
$compiled{$registerName} = qr/\b$registerName\b/;
}
$ret = 0;
if( $str =~ /$compiled{$registerName}/gi )
{
$ret = 1;
}
return $ret;
}
sub PadString
{
local( $str ) = shift;
local( $desiredLen ) = shift;
local( $len ) = length $str;
while( $len < $desiredLen )
{
$str .= " ";
$len++;
}
return $str;
}
sub FixupAllocateFree
{
local( $line ) = shift;
$line =~ s/\&AllocateRegister\s*\(\s*\\(\S+)\s*\)/&AllocateRegister( \\$1, \"\\$1\" )/g;
$line =~ s/\&FreeRegister\s*\(\s*\\(\S+)\s*\)/&FreeRegister( \\$1, \"\\$1\" )/g;
$line =~ s/alloc\s+(\S+)\s*/local( $1 ); &AllocateRegister( \\$1, \"\\$1\" );/g;
$line =~ s/free\s+(\S+)\s*/&FreeRegister( \\$1, \"\\$1\" );/g;
return $line;
}
sub TranslateDXKeywords
{
local( $line ) = shift;
$line =~ s/\bENDIF\b/endif/g;
$line =~ s/\bIF\b/if/g;
$line =~ s/\bELSE\b/else/g;
if ( $g_xbox )
{
$line =~ s/^\s*vs\.1\.[0-9]/xvs.1.1/i;
}
return $line;
}
# This is used to make the generated pl files all pretty.
sub GetLeadingWhiteSpace
{
local( $str ) = shift;
if( $str =~ m/^;\S/ || $str =~ m/^; \S/ )
{
return "";
}
$str =~ s/^;/ /g; # count a leading ";" as whitespace as far as this is concerned.
$str =~ m/^(\s*)/;
return $1;
}
$g_dx9 = 1;
$shaderoutputdir = "shaders";
$g_SourceDir = "..\\..";
while( 1 )
{
$filename = shift;
if ( $filename =~ m/-source/i )
{
$g_SourceDir = shift;
}
elsif( $filename =~ m/-xbox/i )
{
$g_xbox = 1;
$g_dx9 = 0;
}
elsif( $filename =~ m/-shaderoutputdir/i )
{
$shaderoutputdir = shift;
}
else
{
last;
}
}
#
# Get the shader binary version number from a header file.
#
open FILE, "<$g_SourceDir\\public\\materialsystem\\shader_vcs_version.h" || die;
while( $line = )
{
if( $line =~ m/^\#define\s+SHADER_VCS_VERSION_NUMBER\s+(\d+)\s*$/ )
{
$shaderVersion = $1;
last;
}
}
if( !defined $shaderVersion )
{
die "couldn't get shader version from shader_vcs_version.h";
}
close FILE;
if( $g_xbox )
{
$vshtmp = "vshtmp_xbox";
}
else
{
$vshtmp = "vshtmp9";
}
if( !stat $vshtmp )
{
mkdir $vshtmp, 0777 || die $!;
}
# suck in all files, including $include files.
@input = &ReadInputFile( $filename );
sub CalcNumCombos
{
local( $i, $numCombos );
$numCombos = 1;
for( $i = 0; $i < scalar( @dynamicDefineNames ); $i++ )
{
$numCombos *= $dynamicDefineMax[$i] - $dynamicDefineMin[$i] + 1;
}
for( $i = 0; $i < scalar( @staticDefineNames ); $i++ )
{
$numCombos *= $staticDefineMax[$i] - $staticDefineMin[$i] + 1;
}
return $numCombos;
}
sub CalcNumDynamicCombos
{
local( $i, $numCombos );
$numCombos = 1;
for( $i = 0; $i < scalar( @dynamicDefineNames ); $i++ )
{
$numCombos *= $dynamicDefineMax[$i] - $dynamicDefineMin[$i] + 1;
}
return $numCombos;
}
# READ THE TOP OF THE FILE TO FIND SHADER COMBOS
foreach $_ ( @input )
{
next if( m/^\s*$/ );
# last if( !m,^//, );
s,^//\s*,,;
if( m/\s*STATIC\s*\:\s*\"(.*)\"\s+\"(\d+)\.\.(\d+)\"/ )
{
local( $name, $min, $max );
$name = $1;
$min = $2;
$max = $3;
# print "\"$name\" \"$min..$max\"\n";
if (/\[(.*)\]/)
{
$platforms=$1;
next if ( ($g_xbox) && (!($platforms=~/XBOX/i)) );
next if ( (!$g_xbox) && (!($platforms=~/PC/i)) );
}
push @staticDefineNames, $name;
push @staticDefineMin, $min;
push @staticDefineMax, $max;
}
elsif( m/\s*DYNAMIC\s*\:\s*\"(.*)\"\s+\"(\d+)\.\.(\d+)\"/ )
{
local( $name, $min, $max );
$name = $1;
$min = $2;
$max = $3;
if (/\[(.*)\]/)
{
$platforms=$1;
next if ( ($g_xbox) && (!($platforms=~/XBOX/i)) );
next if ( (!$g_xbox) && (!($platforms=~/PC/i)) );
}
# print "\"$name\" \"$min..$max\"\n";
push @dynamicDefineNames, $name;
push @dynamicDefineMin, $min;
push @dynamicDefineMax, $max;
}
}
# READ THE WHOLE FILE AND FIND SKIP STATEMENTS
foreach $_ ( @input )
{
if( m/^\s*\#\s*SKIP\s*\:\s*(.*\S+)\s*\; LINEINFO.*$/ )
{
$perlskipcode .= "(" . $1 . ")||";
push @perlskipcodeindividual, $1;
}
}
if( defined $perlskipcode )
{
$perlskipcode .= "0";
$perlskipcode =~ s/\n//g;
}
else
{
$perlskipcode = "0";
}
#print $perlskipcode . "\n";
if ( $g_xbox )
{
# add mandatory epilogue code
push @outputProgram, "push \@output, \"" . "#pragma screenspace" . "\\n\";\n";
}
# Translate the input into a perl program that'll unroll everything and
# substitute variables.
while( $inputLine = shift @input )
{
$inputLine =~ s/\n//g;
# leave out lines that are only whitespace.
if( $inputLine =~ m/^\s*; LINEINFO.*$/ )
{
next;
}
local( $inputLineNoLineNum ) = $inputLine;
$inputLineNoLineNum =~ s/; LINEINFO.*//gi;
if( &IsPerl( $inputLineNoLineNum ) )
{
$inputLineNoLineNum = &FixupAllocateFree( $inputLineNoLineNum );
push @outputProgram, $inputLineNoLineNum . "\n";
}
else
{
# make asm lines that have quotes in them not barf.
$inputLine =~ s/\"/\\\"/g;
$inputLine = &TranslateDXKeywords( $inputLine );
push @outputProgram, &GetLeadingWhiteSpace( $inputLine ) . "push \@output, \"" .
$inputLine . "\\n\";\n";
}
}
if ( $g_xbox )
{
# add mandatory prologue code
push @outputProgram, "push \@output, \"" . "mul oPos.xyz, r12, \$SHADER_VIEWPORT_CONST_SCALE +rcc r1.x, r12.w" . "\\n\";\n";
push @outputProgram, "push \@output, \"" . "mad oPos.xyz, r12, r1.x, \$SHADER_VIEWPORT_CONST_OFFSET" . "\\n\";\n";
}
$outputProgram = join "", @outputProgram;
$filename_base = $filename;
$filename_base =~ s/\.vsh//i;
open DEBUGOUT, ">$vshtmp" . "/$filename_base.pl" || die;
print DEBUGOUT $outputProgram;
close DEBUGOUT;
# Make a function called OutputProgram()
$bigProg = "sub OutputProgram { " . $outputProgram . "}";
eval( $bigProg );
#print $outputProgram;
#push @finalheader, "// hack to force dependency checking\n";
#push @finalheader, "\#ifdef NEVER\n";
#push @finalheader, "\#include \"" . $filename_base . "\.vsh\"\n";
#push @finalheader, "\#include \"..\\..\\devtools\\bin\\vsh_prep.pl\"\n";
#push @finalheader, "\#endif\n";
%g_TimingBlocks = ();
$main_start_time = SampleTime();
$numCombos = &CalcNumCombos();
$numDynamicCombos = &CalcNumDynamicCombos();
#print "$numCombos total combos\n";
#print "$numDynamicCombos dynamic combos\n";
#print $numCombos / $numDynamicCombos . " static combos\n";
# Write out the C++ helper class for picking shader combos
$fxc_filename = $filename_base;
&WriteStaticHelperClasses();
&WriteDynamicHelperClasses();
# Create a subroutine out of $perlskipcode
$perlskipfunc = "sub SkipCombo { return $perlskipcode; }\n";
#print $perlskipfunc;
eval $perlskipfunc;
&CreateFuncToSetPerlVars();
# Write the inc file that has indexing helpers, etc.
local( *FILE );
if( !open FILE, ">$vshtmp/$filename_base" . ".inc" )
{
die "\n\nUnable to open $vshtmp/$filename_base for writing\n\n";
}
print FILE @outputHeader;
close FILE;
undef @outputHeader;
# Run the output program for all the combinations of bones and lights.
print "$filename_base.vsh\n";
for( $i = 0; $i < $numCombos; $i++ )
{
# print "combo $i\n";
&SetPerlVarsFunc( $i );
local( $compileFailed );
$ret = &SkipCombo;
if( !defined $ret )
{
die "$@\n";
}
if( $ret )
{
# skip this combo!
$compileFailed = 1;
$numSkipped++;
next;
}
$start = SampleTime();
$g_usesPos = 0;
$g_usesPosFlex = 0;
$g_usesBoneWeights = 0;
$g_usesBoneIndices = 0;
$g_usesNormal = 0;
$g_usesNormalFlex = 0;
$g_usesColor = 0;
$g_usesSpecular = 0;
$g_usesTexCoord0 = 0;
$g_usesTexCoord1 = 0;
$g_usesTexCoord2 = 0;
$g_usesTexCoord3 = 0;
$g_usesTangentS = 0;
$g_usesTangentT = 0;
$g_usesUserData = 0;
undef @output;
$g_TimingBlocks{"inner1"} += SampleTime() - $start;
$eval_start_time = SampleTime();
&OutputProgram();
$eval_total_time += (SampleTime() - $eval_start_time);
$start = SampleTime();
# Strip out comments once so we don't have to do it in all the UsesRegister calls.
@stripped = @output;
map
{
$_ =~ s/;.*//gi;
$_ =~ s/\/\/.*//gi;
} @stripped;
my $strippedStr = join( "", @stripped );
$g_TimingBlocks{"inner2"} += SampleTime() - $start;
$start = SampleTime();
# Have to make another pass through after we know which v registers are used. . yuck.
$g_usesPos = &UsesRegister( $vPos, $strippedStr );
if( $g_xbox )
{
$g_usesPosFlex = &UsesRegister( $vPosFlex, $strippedStr );
$g_usesNormalFlex = &UsesRegister( $vNormalFlex, $strippedStr );
}
$g_usesBoneWeights = &UsesRegister( $vBoneWeights, $strippedStr );
$g_usesBoneIndices = &UsesRegister( $vBoneIndices, $strippedStr );
$g_usesNormal = &UsesRegister( $vNormal, $strippedStr );
$g_usesColor = &UsesRegister( $vColor, $strippedStr );
$g_usesSpecular = &UsesRegister( $vSpecular, $strippedStr );
$g_usesTexCoord0 = &UsesRegister( $vTexCoord0, $strippedStr );
$g_usesTexCoord1 = &UsesRegister( $vTexCoord1, $strippedStr );
$g_usesTexCoord2 = &UsesRegister( $vTexCoord2, $strippedStr );
$g_usesTexCoord3 = &UsesRegister( $vTexCoord3, $strippedStr );
$g_usesTangentS = &UsesRegister( $vTangentS, $strippedStr );
$g_usesTangentT = &UsesRegister( $vTangentT, $strippedStr );
$g_usesUserData = &UsesRegister( $vUserData, $strippedStr );
undef @output;
$g_TimingBlocks{"inner2"} += SampleTime() - $start;
$eval_start_time = SampleTime();
# Running OutputProgram generates $outfilename
&OutputProgram();
$eval_total_time += (SampleTime() - $eval_start_time);
$start = SampleTime();
&CheckUnfreedRegisters();
for( $j = 0; $j < scalar( @output ); $j++ )
{
# remove whitespace from the beginning of each line.
$output[$j] =~ s/^\s+//;
# remove LINEINFO from empty lines.
$output[$j] =~ s/^; LINEINFO.*//;
}
$g_TimingBlocks{"inner3"} += SampleTime() - $start;
$start = SampleTime();
$outfilename_base = $filename_base . "_" . $i;
# $outfilename is the name of the file generated from executing the perl code
# for this shader. This file is generated once per combo.
# We will assemble this shader with vsa.exe.
$outfilename = "$vshtmp\\" . $outfilename_base . ".tmp";
# $outhdrfilename = "$vshtmp\\" . $outfilename_base . ".h";
# unlink $outhdrfilename;
open OUTPUT, ">$outfilename" || die;
print OUTPUT @output;
close OUTPUT;
$g_TimingBlocks{"inner4"} += SampleTime() - $start;
$start = SampleTime();
local( $instructionCount ) = &CountInstructions( @output );
$g_TimingBlocks{"inner5"} += SampleTime() - $start;
local( $debug );
$debug = 1;
# for( $debug = 1; $debug >= 0; $debug-- )
{
# assemble the vertex shader
unlink "shader$i.o";
if( $g_xbox )
{
$vsa = "xsasm";
$vsadebug = "$vsa /nologo /D _XBOX=1 $outfilename shader$i.o";
$vsanodebug = "$vsa /nologo /D _XBOX=1 $outfilename shader$i.o";
}
else
{
$vsa = "..\\..\\dx9sdk\\utilities\\vsa";
$vsadebug = "$vsa /nologo /Foshader$i.o $outfilename";
$vsanodebug = "$vsa /nologo /Foshader$i.o $outfilename";
}
$vsa_start_time = SampleTime();
if( $debug )
{
# print $vsadebug . "\n";
@vsaoutput = `$vsadebug 2>&1`;
# print @vsaoutput;
}
else
{
@vsaoutput = `$vsanodebug 2>&1`;
}
$vsa_total_time += SampleTime() - $vsa_start_time;
$start = SampleTime();
&TranslateErrorMessages( @vsaoutput );
$g_TimingBlocks{"inner6"} += SampleTime() - $start;
push @finalheader, @hdr;
}
}
$main_total_time = SampleTime() - $main_start_time;
# stick info about the shaders at the end of the inc file.
push @finalheader, "static PrecompiledShaderByteCode_t $filename_base" . "_vertex_shaders[] = {\n";
for( $i = 0; $i < $numCombos; $i++ )
{
$outfilename_base = $filename_base . "_" . $i;
push @finalheader, "{ $outfilename_base, sizeof( $outfilename_base ) },\n";
}
push @finalheader, "};\n";
push @finalheader, "struct $filename_base" . "_VertexShader_t : public PrecompiledShader_t\n";
push @finalheader, "{\n";
push @finalheader, "\t$filename_base" . "_VertexShader_t()\n";
push @finalheader, "\t{\n";
push @finalheader, "\t\tm_nFlags = 0;\n";
$flags = 0;
#push @finalheader, "\t\tppVertexShaders = $filename_base" . "_vertex_shaders;\n";
push @finalheader, "\t\tm_pByteCode = $filename_base" . "_vertex_shaders;\n";
push @finalheader, "\t\tm_pName = \"$filename_base\";\n";
push @finalheader, "\t\tm_nShaderCount = " . ( $maxNumBones + 1 ) * $totalFogCombos * $totalLightCombos . ";\n";
push @finalheader, "\t\tm_nDynamicCombos = m_nShaderCount;\n";
push @finalheader, "\t\tGetShaderDLL()->InsertPrecompiledShader( PRECOMPILED_VERTEX_SHADER, this );\n";
push @finalheader, "\t}\n";
push @finalheader, "\tvirtual const PrecompiledShaderByteCode_t &GetByteCode( int shaderID )\n";
push @finalheader, "\t{\n";
push @finalheader, "\t\treturn m_pByteCode[shaderID];\n";
push @finalheader, "\t}\n";
push @finalheader, "};\n";
push @finalheader, "static $filename_base" . "_VertexShader_t $filename_base" . "_VertexShaderInstance;\n";
# Write the final header file with the compiled vertex shader programs.
$finalheadername = "$vshtmp\\" . $filename_base . ".inc";
#print "writing $finalheadername\n";
#open FINALHEADER, ">$finalheadername" || die;
#print FINALHEADER @finalheader;
#close FINALHEADER;
&MakeDirHier( "$shaderoutputdir/vsh" );
open COMPILEDSHADER, ">$shaderoutputdir/vsh/$filename_base.vcs" || die;
binmode( COMPILEDSHADER );
#
# Write out the part of the header that we know. . we'll write the rest after writing the object code.
#
# version
print COMPILEDSHADER pack "i", $shaderVersion;
# totalCombos
print COMPILEDSHADER pack "i", $numCombos;
# dynamic combos
print COMPILEDSHADER pack "i", $numDynamicCombos;
# flags
print COMPILEDSHADER pack "I", $flags;
# centroid mask
print COMPILEDSHADER pack "I", 0;
# reference size
print COMPILEDSHADER pack "I", 0;
my $beginningOfDir = tell COMPILEDSHADER;
# Write out a blank directionary. . we'll fill it in later.
for( $i = 0; $i < $numCombos; $i++ )
{
# offset from beginning of file.
print COMPILEDSHADER pack "i", 0;
# size
print COMPILEDSHADER pack "i", 0;
}
my $startByteCode = tell COMPILEDSHADER;
my @byteCodeStart;
my @byteCodeSize;
# Write out the shader object code.
for( $shaderCombo = 0; $shaderCombo < $numCombos; $shaderCombo++ )
{
my $filename = "shader$shaderCombo\.o";
my $filesize = (stat $filename)[7];
$byteCodeStart[$shaderCombo] = tell COMPILEDSHADER;
$byteCodeSize[$shaderCombo] = $filesize;
open SHADERBYTECODE, "<$filename" || die;
binmode SHADERBYTECODE;
my $bin;
my $numread = read SHADERBYTECODE, $bin, $filesize;
# print "filename: $filename numread: $numread filesize: $filesize\n";
close SHADERBYTECODE;
unlink $filename;
print COMPILEDSHADER $bin;
}
# Seek back to the directory and write it out.
seek COMPILEDSHADER, $beginningOfDir, 0;
for( $i = 0; $i < $numCombos; $i++ )
{
# offset from beginning of file.
print COMPILEDSHADER pack "i", $byteCodeStart[$i];
# size
print COMPILEDSHADER pack "i", $byteCodeSize[$i];
}
close COMPILEDSHADER;
$total_time = SampleTime() - $total_start_time;
if ( $ShowTimers )
{
print "\n\n";
print sprintf( "Main loop time : %0.4f sec, (%0.2f%%)\n", $main_total_time, 100*$main_total_time / $total_time );
print sprintf( "Inner1 time : %0.4f sec, (%0.2f%%)\n", $inner1_total_time, 100*$inner1_total_time / $total_time );
print sprintf( "VSA time : %0.4f sec, (%0.2f%%)\n", $vsa_total_time, 100*$vsa_total_time / $total_time );
print sprintf( "eval() time : %0.4f sec, (%0.2f%%)\n", $eval_total_time, 100*$eval_total_time / $total_time );
print sprintf( "UsesRegister time: %0.4f sec, (%0.2f%%)\n", $usesr_total_time, 100*$usesr_total_time / $total_time );
foreach $key ( keys %g_TimingBlocks )
{
print sprintf( "$key time: %0.4f sec, (%0.2f%%)\n", $g_TimingBlocks{$key}, 100*$g_TimingBlocks{$key} / $total_time );
}
print sprintf( "Total time : %0.4f sec\n", $total_time );
}