obs/Source/D3D10ShaderProcessor.cpp

594 lines
22 KiB
C++

/********************************************************************************
Copyright (C) 2012 Hugh Bailey <obs.jim@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
********************************************************************************/
#include "Main.h"
//another stripped down code processor from my game engine
#define PeekAtAToken(str) if(!GetNextToken(str, TRUE)) {return FALSE;}
#define HandMeAToken(str) if(!GetNextToken(str)) {return FALSE;}
#define EscapeLikeTheWind(gototoken) {if(!GotoToken(gototoken, TRUE)) {return FALSE;} continue;}
#define ExpectToken(expecting, gototoken) {if(!GetNextToken(curToken)) {return FALSE;} if(curToken != expecting) {if(!GotoToken(gototoken, TRUE)) {return FALSE;} continue;}}
#define ExpectTokenIgnore(expecting) {if(!GetNextToken(curToken)) {return FALSE;} if(curToken != expecting) {continue;}}
CTSTR validSemanticTStrings[] = {TEXT("SV_Position"), TEXT("NORMAL"), TEXT("COLOR"), TEXT("TANGENT"), TEXT("TEXCOORD")};
LPCSTR validSemanticStrings[] = {"SV_Position", "NORMAL", "COLOR", "TANGENT", "TEXCOORD"};
struct SemanticInfo
{
LPCSTR lpName;
UINT index;
};
bool GetSemanticInfo(const String &strSemantic, SemanticInfo &info)
{
for(UINT i=0; i<5; i++)
{
UINT semanticLen = slen(validSemanticTStrings[i]);
if(scmpi_n(strSemantic, validSemanticTStrings[i], semanticLen) == 0)
{
if(strSemantic[semanticLen] && isdigit(strSemantic[semanticLen]))
info.index = strSemantic[semanticLen]-'0';
else
info.index = 0;
info.lpName = validSemanticStrings[i];
return true;
}
}
return false;
}
BOOL ShaderProcessor::ProcessShader(CTSTR input, CTSTR filename)
{
String curToken;
BOOL bError = FALSE;
SetCodeStart(input);
TSTR lpLastPos = lpTemp;
DWORD curInsideCount = 0;
BOOL bNewCodeLine = TRUE;
while(GetNextToken(curToken))
{
TSTR lpCurPos = lpTemp-curToken.Length();
if(curToken[0] == '{')
++curInsideCount;
else if(curToken[0] == '}')
--curInsideCount;
else if(curToken[0] == '(')
++curInsideCount;
else if(curToken[0] == ')')
--curInsideCount;
else if(curToken[0] == '#') //preprocessor
{
HandMeAToken(curToken);
if(scmpi_n(curToken, TEXT("include"), 7) == 0)
{
GetNextToken(curToken);
if(curToken[0] == '<')
EscapeLikeTheWind(TEXT(">")); //TODO: handle #include <foo> directives
String parent(filename);
int num = parent.NumTokens('/');
String loadFile = curToken.Mid(1, curToken.Length()-1);
parent.FindReplace(parent.GetTokenOffset(num-1, '/'), loadFile);
XFile ShaderFile;
if(!ShaderFile.Open(parent, XFILE_READ, XFILE_OPENEXISTING))
continue;
String strShader;
ShaderFile.ReadFileToString(strShader);
ProcessShader(strShader, parent);
}
}
else if(!curInsideCount && bNewCodeLine) //not inside any code, so this is some sort of declaration (function/struct/var)
{
if(curToken == TEXT("class"))
{
while(GetNextToken(curToken))
{
if(curToken[0] == '{')
curInsideCount++;
else if(curToken[0] == '}')
curInsideCount--;
else if(curToken[0] == ';')
if(curInsideCount == 0)
continue;
}
}
else if(curToken == TEXT("struct"))
{
//try to see if this is the vertex definition structure
bool bFoundDefinitionStruct = false;
HandMeAToken(curToken);
ExpectTokenIgnore(TEXT("{"));
curInsideCount = 1;
do
{
HandMeAToken(curToken);
if(curToken.Length() <= 6 && scmpi_n(curToken, TEXT("float"), 5) == 0)
{
String strType = curToken;
String strName;
HandMeAToken(strName);
HandMeAToken(curToken);
if(curToken[0] != ':') //cancel if not a vertex definition structure
{
bFoundDefinitionStruct = false;
break;
}
String strSemantic;
HandMeAToken(strSemantic);
SemanticInfo semanticInfo;
if(!GetSemanticInfo(strSemantic, semanticInfo))
{
bFoundDefinitionStruct = false;
break;
}
D3D10_INPUT_ELEMENT_DESC inputElement;
inputElement.SemanticName = semanticInfo.lpName;
inputElement.SemanticIndex = semanticInfo.index;
inputElement.InputSlot = 0;
inputElement.AlignedByteOffset = 0;
inputElement.InputSlotClass = D3D10_INPUT_PER_VERTEX_DATA;
inputElement.InstanceDataStepRate = 0;
if(strSemantic.CompareI(TEXT("color")))
inputElement.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
else
{
switch(strType[5])
{
case 0: inputElement.Format = DXGI_FORMAT_R32_FLOAT; break;
case '2': inputElement.Format = DXGI_FORMAT_R32G32_FLOAT; break;
case '3': inputElement.Format = DXGI_FORMAT_R32G32B32A32_FLOAT; break; //todo: check this some time
case '4': inputElement.Format = DXGI_FORMAT_R32G32B32A32_FLOAT; break;
}
}
ExpectToken(TEXT(";"), TEXT(";"));
PeekAtAToken(curToken);
generatedLayout << inputElement;
bFoundDefinitionStruct = true;
}
else
{
bFoundDefinitionStruct = false;
break; //vertex definition structures should really only ever have float values
}
} while (curToken[0] != '}');
//set up the slots so they match up with vertex buffers
if(bFoundDefinitionStruct)
{
UINT curSlot = 0;
for(UINT i=0; i<generatedLayout.Num(); i++)
{
if(stricmp(generatedLayout[i].SemanticName, "SV_Position") == 0)
{
generatedLayout[i].InputSlot = curSlot++;
break;
}
}
for(UINT i=0; i<generatedLayout.Num(); i++)
{
if(stricmp(generatedLayout[i].SemanticName, "NORMAL") == 0)
{
generatedLayout[i].InputSlot = curSlot++;
bHasNormals = true;
break;
}
}
for(UINT i=0; i<generatedLayout.Num(); i++)
{
if(stricmp(generatedLayout[i].SemanticName, "COLOR") == 0)
{
generatedLayout[i].InputSlot = curSlot++;
bHasColors = true;
break;
}
}
for(UINT i=0; i<generatedLayout.Num(); i++)
{
if(stricmp(generatedLayout[i].SemanticName, "TANGENT") == 0)
{
generatedLayout[i].InputSlot = curSlot++;
bHasTangents = true;
break;
}
}
bool bFoundTexCoord;
do
{
bFoundTexCoord = false;
for(UINT i=0; i<generatedLayout.Num(); i++)
{
if(generatedLayout[i].SemanticIndex == numTextureCoords && stricmp(generatedLayout[i].SemanticName, "TEXCOORD") == 0)
{
generatedLayout[i].InputSlot = curSlot++;
numTextureCoords++;
bFoundTexCoord = true;
break;
}
}
} while(bFoundTexCoord);
}
}
else if( (curToken != TEXT("const")) &&
(curToken != TEXT("void")) &&
(curToken != TEXT(";")) )
{
TSTR lpSavedPos = lpTemp;
String savedToken = curToken;
if(curToken == TEXT("uniform"))
HandMeAToken(curToken);
String strType = curToken;
String strName;
HandMeAToken(strName);
PeekAtAToken(curToken);
if(curToken[0] != '(') //verified variable
{
if(strType.CompareI(TEXT("samplerstate")))
{
ShaderSampler &curSampler = *Samplers.CreateNew();
curSampler.name = strName;
SamplerInfo info;
ExpectToken(TEXT("{"), TEXT(";"));
PeekAtAToken(curToken);
while(curToken != TEXT("}"))
{
String strState;
HandMeAToken(strState);
ExpectToken(TEXT("="), TEXT(";"));
String strValue;
HandMeAToken(strValue);
if(!AddState(info, strState, strValue))
EscapeLikeTheWind(TEXT(";"));
ExpectToken(TEXT(";"), TEXT(";"));
PeekAtAToken(curToken);
}
curSampler.sampler = CreateSamplerState(info);
ExpectToken(TEXT("}"), TEXT("}"));
ExpectTokenIgnore(TEXT(";"));
//----------------------------------------
lpLastPos = lpTemp;
continue;
}
else
{
ShaderParam *param = Params.CreateNew();
param->name = strName;
if(curToken[0] == '[')
{
HandMeAToken(curToken);
HandMeAToken(curToken);
param->arrayCount = tstoi(curToken);
ExpectToken(TEXT("]"), TEXT(";"));
PeekAtAToken(curToken);
}
if(scmpi_n(strType, TEXT("texture"), 7) == 0)
{
TSTR lpType = strType.Array()+7;
supr(lpType);
if (!*lpType ||
(scmp(lpType, TEXT("1D")) && scmp(lpType, TEXT("2D")) && scmp(lpType, TEXT("3D")) && scmp(lpType, TEXT("CUBE")))
)
{
bError = TRUE;
}
param->textureID = nTextures++;
param->type = Parameter_Texture;
strType = TEXT("sampler");
}
else if(scmp_n(strType, TEXT("float"), 5) == 0)
{
CTSTR lpType = strType.Array()+5;
if(*lpType == 0)
param->type = Parameter_Float;
else if(scmpi(lpType, TEXT("2")) == 0)
param->type = Parameter_Vector2;
else if(scmpi(lpType, TEXT("3")) == 0)
param->type = Parameter_Vector3;
else if(scmpi(lpType, TEXT("4")) == 0)
param->type = Parameter_Vector4;
else if(scmpi(lpType, TEXT("3x3")) == 0)
param->type = Parameter_Matrix3x3;
else if(scmpi(lpType, TEXT("4x4")) == 0)
param->type = Parameter_Matrix;
}
else if(scmp(strType, TEXT("int")) == 0)
param->type = Parameter_Int;
else if(scmp(strType, TEXT("bool")) == 0)
param->type = Parameter_Bool;
if(curToken == TEXT("="))
{
HandMeAToken(curToken);
BufferOutputSerializer sOut(param->defaultValue);
if(scmp(strType, TEXT("float")) == 0)
{
HandMeAToken(curToken);
if(!ValidFloatString(curToken))
bError = TRUE;
float fValue = (float)tstof(curToken);
sOut << fValue;
}
else if(scmp(strType, TEXT("int")) == 0)
{
HandMeAToken(curToken);
if(!ValidIntString(curToken))
bError = TRUE;
int iValue = tstoi(curToken);
sOut << iValue;
}
else if(scmp_n(strType, TEXT("float"), 5) == 0)
{
CTSTR lpFloatType = strType.Array()+5;
int floatCount = 0;
if(lpFloatType[0] == '1') floatCount = 1;
else if(lpFloatType[0] == '2') floatCount = 2;
else if(lpFloatType[0] == '3') floatCount = 3;
else if(lpFloatType[0] == '4') floatCount = 4;
else
bError = TRUE;
if(lpFloatType[1] == 'x')
{
if(lpFloatType[2] != '1')
{
if(lpFloatType[2] == '2') floatCount *= 2;
else if(lpFloatType[2] == '3') floatCount *= 3;
else if(lpFloatType[2] == '4') floatCount *= 4;
else
bError = TRUE;
}
}
if(floatCount > 1) {ExpectToken(TEXT("{"), TEXT(";"));}
int j;
for(j=0; j<floatCount; j++)
{
if(j)
{
HandMeAToken(curToken);
if(curToken[0] != ',')
{
bError = TRUE;
break;
}
}
HandMeAToken(curToken);
if(!ValidFloatString(curToken))
{
bError = TRUE;
break;
}
float fValue = (float)tstof(curToken);
sOut << fValue;
}
if(j != floatCount) //processing error occured
{
GotoToken(TEXT(";"));
continue;
}
if(floatCount > 1)
{ExpectToken(TEXT("}"), TEXT(";"));}
}
PeekAtAToken(curToken);
}
}
//--------------------------
lpLastPos = lpTemp;
bNewCodeLine = FALSE;
continue;
}
lpTemp = lpSavedPos;
curToken = savedToken;
}
}
lpLastPos = lpTemp;
bNewCodeLine = (curToken.IsValid() && ((curToken[0] == ';') || (curToken[0] == '}')));
}
return !bError;
}
#undef ExpectToken
#define ExpectToken(expecting) {if(!GetNextToken(curToken)) {return FALSE;} if(curToken != expecting) {return FALSE;}}
BOOL ShaderProcessor::AddState(SamplerInfo &info, String &stateName, String &stateVal)
{
if(scmpi_n(stateName, TEXT("Address"), 7) == 0)
{
int type = stateName[7]-'U';
GSAddressMode *mode;
switch(type)
{
case 0: mode = &info.addressU; break;
case 1: mode = &info.addressV; break;
case 2: mode = &info.addressW; break;
default: CrashError(TEXT("Invalid shader address type %d"), type);
}
if(stateVal.CompareI(TEXT("Wrap")) || stateVal.CompareI(TEXT("Repeat")))
*mode = GS_ADDRESS_WRAP;
else if(stateVal.CompareI(TEXT("Clamp")) || stateVal.CompareI(TEXT("None")))
*mode = GS_ADDRESS_CLAMP;
else if(stateVal.CompareI(TEXT("Mirror")))
*mode = GS_ADDRESS_MIRROR;
else if(stateVal.CompareI(TEXT("Border")))
*mode = GS_ADDRESS_BORDER;
else if(stateVal.CompareI(TEXT("MirrorOnce")))
*mode = GS_ADDRESS_MIRRORONCE;
}
else if(stateName.CompareI(TEXT("MaxAnisotropy")))
{
info.maxAnisotropy = tstoi(stateVal);
}
else if(stateName.CompareI(TEXT("Filter")))
{
if(stateVal.CompareI(TEXT("Anisotropic")))
info.filter = GS_FILTER_ANISOTROPIC;
else if(stateVal.CompareI(TEXT("Point")) || stateVal.CompareI(TEXT("MIN_MAG_MIP_POINT")))
info.filter = GS_FILTER_POINT;
else if(stateVal.CompareI(TEXT("Linear")) || stateVal.CompareI(TEXT("MIN_MAG_MIP_LINEAR")))
info.filter = GS_FILTER_LINEAR;
else if(stateVal.CompareI(TEXT("MIN_MAG_POINT_MIP_LINEAR")))
info.filter = GS_FILTER_MIN_MAG_POINT_MIP_LINEAR;
else if(stateVal.CompareI(TEXT("MIN_POINT_MAG_LINEAR_MIP_POINT")))
info.filter = GS_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT;
else if(stateVal.CompareI(TEXT("MIN_POINT_MAG_MIP_LINEAR")))
info.filter = GS_FILTER_MIN_POINT_MAG_MIP_LINEAR;
else if(stateVal.CompareI(TEXT("MIN_LINEAR_MAG_MIP_POINT")))
info.filter = GS_FILTER_MIN_LINEAR_MAG_MIP_POINT;
else if(stateVal.CompareI(TEXT("MIN_LINEAR_MAG_POINT_MIP_LINEAR")))
info.filter = GS_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR;
else if(stateVal.CompareI(TEXT("MIN_MAG_LINEAR_MIP_POINT")))
info.filter = GS_FILTER_MIN_MAG_LINEAR_MIP_POINT;
}
else if(stateName.CompareI(TEXT("BorderColor")))
{
if(stateVal[0] == '{')
{
String curToken;
HandMeAToken(curToken);
if(!ValidFloatString(curToken)) {return FALSE;}
info.borderColor.x = (float)tstof(curToken);
//-------------------------------
ExpectToken(TEXT(","));
//-------------------------------
HandMeAToken(curToken);
if(!ValidFloatString(curToken)) {return FALSE;}
info.borderColor.y = (float)tstof(curToken);
//-------------------------------
ExpectToken(TEXT(","));
//-------------------------------
HandMeAToken(curToken);
if(!ValidFloatString(curToken)) {return FALSE;}
info.borderColor.z = (float)tstof(curToken);
//-------------------------------
ExpectToken(TEXT(","));
//-------------------------------
HandMeAToken(curToken);
if(!ValidFloatString(curToken)) {return FALSE;}
info.borderColor.w = (float)tstof(curToken);
//-------------------------------
ExpectToken(TEXT("}"));
}
else if(ValidIntString(stateVal))
info.borderColor = Color4().MakeFromRGBA(tstoi(stateVal));
}
return TRUE;
}