From 15740faf9fe9fe4be08965098bbf2947e096aeeb Mon Sep 17 00:00:00 2001 From: chai Date: Wed, 14 Aug 2019 22:50:43 +0800 Subject: +Unity Runtime code --- Runtime/GfxDevice/d3d/ShaderGenerator.cpp | 948 ++++++++++++++++++++++++++++++ 1 file changed, 948 insertions(+) create mode 100644 Runtime/GfxDevice/d3d/ShaderGenerator.cpp (limited to 'Runtime/GfxDevice/d3d/ShaderGenerator.cpp') diff --git a/Runtime/GfxDevice/d3d/ShaderGenerator.cpp b/Runtime/GfxDevice/d3d/ShaderGenerator.cpp new file mode 100644 index 0000000..b62e5c7 --- /dev/null +++ b/Runtime/GfxDevice/d3d/ShaderGenerator.cpp @@ -0,0 +1,948 @@ +#include "UnityPrefix.h" +#include +#include +#include +#include +#include +#include "ShaderGenerator.h" +#include "Runtime/Utilities/Word.h" + +enum ShaderInputRegister { + kInputPosition, + kInputNormal, + kInputUV0, + kInputUV1, + kInputColor, + kInputCount +}; + +const char* kShaderInputNames[kInputCount] = { + "$IPOS", + "$INOR", + "$IUV0", + "$IUV1", + "$ICOL", +}; + +const char* kShaderInputDecls[kInputCount] = { + "dcl_position", + "dcl_normal", + "dcl_texcoord0", + "dcl_texcoord1", + "dcl_color", +}; + + + +enum ShaderFragmentOptions { + kOptionHasTexMatrix = (1<<0), +}; + +const int kConstantLocations[kConstCount] = { + 0, // kConstMatrixMVP + 4, // kConstMatrixMV + 8, // kConstMatrixMV_IT + 12, // kConstMatrixTexture + 44, // kConstAmbient + 57, // kConstColorMatAmbient + 45, // kConstLightMisc + 46, // kConstMatDiffuse + 47, // kConstMatSpecular + 48, // kConstLightIndexes +}; + +enum CommonDependencies { + kDep_CamSpacePos, + kDep_CamSpaceN, + kDep_ViewVector, + kDep_ReflVector, + kDep_Normal, + kDepCount +}; + + +// -------------------------------------------------------------------------- + +// transform position +const ShaderFragment kVS_Pos = { + (1< 0 + " mul $TMP0.xyz, $TMP0.x, c62[a0.x]\n" // doff = NdotL * lightColor + " mad $O_DIFF.xyz, $TMP0.w, $TMP0, $O_DIFF\n" // diffuse += diff * clamp + " add $O_CNOR.w, $O_CNOR.w, c45.y\n" // index += 4 + "endrep\n" +}; + +const ShaderFragment kVS_Light_Diffuse_Point = { + 0, // input + (1< 0 + " mul $TMP2, $TMP1.x, c62[a0.x]\n" // diff = NdotL * lightColor + " mul $TMP2, $TMP0.w, $TMP2\n" // diff *= attenuation + " mad $O_DIFF.xyz, $TMP1.y, $TMP2, $O_DIFF\n" // diffuse += diff * clamp + " add $O_CNOR.w, $O_CNOR.w, c45.y\n" // index += 4 + "endrep\n" +}; + + + +const ShaderFragment kVS_Light_Diffuse_Spot = { + 0, // input + (1< 0 + " mul $TMP2, $TMP1.w, c62[a0.x]\n" // diff = NdotL * lightColor + " mul $TMP2, $TMP0.w, $TMP2\n" // diff *= attenuation + " mad $O_DIFF.xyz, $TMP1.x, $TMP2, $O_DIFF\n" // diffuse += diff * clamp + " add $O_CNOR.w, $O_CNOR.w, c45.y\n" // index += 4 + "endrep\n" +}; + + +const ShaderFragment kVS_Light_Specular_Pre = { + 0, // input + (1< 0 + " mul $TMP1, $TMP1.x, c62[a0.x]\n" // diff = NdotL * lightColor + " mad $O_DIFF.xyz, $TMP0.w, $TMP1, $O_DIFF\n" // diffuse += diff * clamp + // spec + " add $TMP0.xyz, $TMP0, $O_VIEW\n" // L + V + " nrm $TMP1.xyz, $TMP0\n" // H = normalize(L + V) + " dp3 $TMP1.w, $TMP1, $O_CNOR\n" // H dot N + " max $TMP1.w, $TMP1.w, c45.x\n" // sp = max(H dot N, 0) + " pow $TMP1.w, $TMP1.w, c47.w\n" // sp = pow(sp, exponent) + " mul $TMP1.w, $TMP1.w, $TMP0.w\n" // sp *= clamp + " mad $O_SPEC.xyz, $TMP1.w, c62[a0.x], $O_SPEC\n" // spec += sp * lightColor + + " add $O_CNOR.w, $O_CNOR.w, c45.y\n" // index += 4 + "endrep\n" +}; + + +const ShaderFragment kVS_Light_Specular_Point = { + 0, // input + (1< 0 + " mul $TMP2, $TMP0.x, c62[a0.x]\n" // diff = NdotL * lightColor + " mul $TMP2, $TMP0.w, $TMP2\n" // diff *= attenuation + " mad $O_DIFF.xyz, $TMP0.y, $TMP2, $O_DIFF\n" // diffuse += diff * clamp + // spec + " add $TMP2.xyz, $TMP1, $O_VIEW\n" // L + V + " nrm $TMP1.xyz, $TMP2\n" // H = normalize(L + V) + " dp3 $TMP1.w, $TMP1, $O_CNOR\n" // H dot N + " max $TMP1.w, $TMP1.w, c45.x\n" // sp = max(H dot N, 0) + " pow $TMP1.w, $TMP1.w, c47.w\n" // sp = pow(sp, exponent) + " mul $TMP1.w, $TMP1.w, $TMP0.w\n" // sp *= attenuation + " mul $TMP1.w, $TMP1.w, $TMP0.y\n" // sp *= clamp + " mad $O_SPEC.xyz, $TMP1.w, c62[a0.x], $O_SPEC\n" // spec += sp * lightColor + + " add $O_CNOR.w, $O_CNOR.w, c45.y\n" // index += 4 + "endrep\n" +}; + +const ShaderFragment kVS_Light_Specular_Spot = { + 0, // input + (1< 0 + " mul $TMP2, $TMP1.w, c62[a0.x]\n" // diff = NdotL * lightColor + " mul $TMP2, $TMP0.w, $TMP2\n" // diff *= attenuation + " mad $O_DIFF.xyz, $TMP0.x, $TMP2, $O_DIFF\n" // diffuse += diff * clamp + // spec + " add $TMP2.xyz, $TMP1, $O_VIEW\n" // L + V + " nrm $TMP1.xyz, $TMP2\n" // H = normalize(L + V) + " dp3 $TMP1.w, $TMP1, $O_CNOR\n" // H dot N + " max $TMP1.w, $TMP1.w, c45.x\n" // sp = max(H dot N, 0) + " pow $TMP2.x, $TMP1.w, c47.w\n" // sp = pow(sp, exponent) + " mul $TMP2.x, $TMP2.x, $TMP0.w\n" // sp *= attenuation + " mul $TMP2.x, $TMP2.x, $TMP0.x\n" // sp *= clamp + " mad $O_SPEC.xyz, $TMP2.x, c62[a0.x], $O_SPEC\n" // spec += sp * lightColor + + " add $O_CNOR.w, $O_CNOR.w, c45.y\n" // index += 4 + "endrep\n" +}; + + +const ShaderFragment kVS_Out_Diffuse_Lighting = { + 0, // input + (1<='A' && c<='Z' || c>='0' && c<='9'; +} + +static const char* SkipTokens( const char* p, int count ) { + while( count-- ) { + while( IsAlNum(*p++) ) ; + if( *p == 0 ) + return p; + ++p; + } + return p; +} + +static std::string ExtractToken( const char** text ) { + const char* ptr = *text; + char c = *ptr; + while( IsAlNum(c) ) { + ++ptr; + c = *ptr; + } + + if( ptr == *text ) + return std::string(); + + // result + std::string res(*text, ptr); + + // skip space after result + ++ptr; + *text = ptr; + + return res; +} + +void ShaderGenerator::AddFragment( const ShaderFragment* fragment, const char* inputNames, int param ) +{ + // is already added? + FragmentData data(fragment, inputNames, param); + for( int i = 0; i < m_FragmentCount; ++i ) { + if( m_Fragments[i] == data ) + return; + } + + // add it's dependencies first + if( fragment->dependencies ) { + for( int i = 0; i < kDepCount; ++i ) { + // has this dependency? + if( !(fragment->dependencies & (1< SavedRegisters; + +static inline int FindSavedRegister( const SavedRegisters& regs, const std::string& name ) +{ + int n = regs.size(); + for( int i = 0; i < n; ++i ) + if( regs[i].name == name ) + return i; + return -1; +} + +void ShaderGenerator::GenerateShader( std::string& output, unsigned int& usedConstants ) +{ + unsigned int usedConstantsMask = 0; + + output.clear(); + output.reserve(1024); + //debug.clear(); + + // shader input mappings + int inputMapping[kInputCount]; + for( int i = 0; i < kInputCount; ++i ) + inputMapping[i] = -1; + int usedInputStack[kInputCount]; + int usedInputs = 0; + + // saved registers across fragments + SavedRegisters savedRegisters; + + // go over fragments and figure out inputs, saved registers and used constants + int maxTemps = 0; + for( int fi = 0; fi < m_FragmentCount; ++fi ) { + const ShaderFragment& frag = *m_Fragments[fi].fragment; + + // fragment vertex inputs + for( int i = 0; i < kInputCount; ++i ) { + // does fragment use this input? + if( frag.inputs & (1<='0' && token[1]<='9'); + int index = token[1]-'0'; + const char* inputNames = m_Fragments[fi].inputNames; + inputNames = SkipTokens( inputNames, index ); + token = ExtractToken(&inputNames); + } + token = "$O_" + token; + + //TODO: check that text has that token. + //TODO: check that text has no $O_ tokens that are not in the input + int savedIndex = FindSavedRegister( savedRegisters, token ); + assert(savedIndex != -1); + assert(savedRegisters[savedIndex].lastUse <= fi); + savedRegisters[savedIndex].lastUse = fi; + } + } + + maxTemps = std::max(maxTemps, frag.temps); + + // used constants + usedConstantsMask |= frag.constants; + } + + assert( savedRegisters.size() <= kMaxSavedRegisters ); + + // assign register indices to saved registers + int mapFragmentRegister[kMaxShaderFragments][kMaxTempRegisters]; // [fragment][index] = used or not? + memset(mapFragmentRegister, 0, sizeof(mapFragmentRegister)); + for( size_t i = 0; i < savedRegisters.size(); ++i ) { + // find unused register over whole lifetime, and assign it + SavedRegister& sr = savedRegisters[i]; + assert(sr.regIndex == -1); + for( int regIndex = 0; regIndex < kMaxTempRegisters; ++regIndex ) { + bool unused = true; + for( int fi = sr.firstUse; fi <= sr.lastUse; ++fi ) { + if( mapFragmentRegister[fi][regIndex] != 0 ) { + unused = false; + break; + } + } + if( unused ) { + for( int fi = sr.firstUse; fi <= sr.lastUse; ++fi ) + mapFragmentRegister[fi][regIndex] = 1; + sr.regIndex = regIndex; + break; + } + } + assert(sr.regIndex != -1); + } + + // generate prolog with declarations + output += "vs_2_0\n"; + for( int i = 0; i < usedInputs; ++i ) { + output += kShaderInputDecls[usedInputStack[i]]; + output += " v"; + assert(i<=9); + output += ('0' + i); + output += '\n'; + } + + // go over fragments, transform register names and output + for( int fi = 0; fi < m_FragmentCount; ++fi ) { + const ShaderFragment& frag = *m_Fragments[fi].fragment; + int param = m_Fragments[fi].param; + + output += '\n'; + std::string text = frag.text; + + std::string regname("r0"); + std::string regname2("r00"); + + // input registers + regname[0] = 'v'; + for( int i = 0; i < usedInputs; ++i ) { + int inputIndex = usedInputStack[i]; + assert(i<=9); + regname[1] = '0' + i; + replace_string(text, kShaderInputNames[inputIndex], regname); + } + + // fragment inputs + if( frag.ins ) { + const char* inputs = frag.ins; + std::string token; + while( !(token = ExtractToken(&inputs)).empty() ) { + std::string searchName; + std::string savedName; + // a parametrized token? + if( token[0] == '$' ) { + assert(token.size()==2); + assert(token[1]>='0' && token[1]<='9'); + int index = token[1]-'0'; + const char* inputNames = m_Fragments[fi].inputNames; + inputNames = SkipTokens( inputNames, index ); + token = ExtractToken(&inputNames); + searchName = std::string("$I_") + char('0'+index); + } else { + searchName = "$O_" + token; + } + savedName = "$O_" + token; + + // Assign register index to this saved reg + SavedRegisters::iterator it, itEnd = savedRegisters.end(); + for( it = savedRegisters.begin(); it != itEnd; ++it ) { + const SavedRegister& sr = *it; + if( sr.name == savedName ) + { + // replace with register value + regname[0] = 'r'; + assert(sr.regIndex<=9); + regname[1] = '0' + sr.regIndex; + replace_string(text, searchName, regname); + break; + } + } + assert( it != itEnd ); + } + } + + // saved registers + if( frag.outs ) { + regname[0] = 'r'; + SavedRegisters::iterator it, itEnd = savedRegisters.end(); + for( it = savedRegisters.begin(); it != itEnd; ++it ) { + const SavedRegister& sr = *it; + assert(sr.regIndex<=9); + regname[1] = '0' + sr.regIndex; + replace_string(text, sr.name, regname); + } + } + + // fragment-private temporary registers + regname[0] = 'r'; + regname2[0] = 'r'; + std::string tmpname("$TMP0"); + int regIndex = 0; + for( int i = 0; i < frag.temps; ++i ) { + assert(i<=9); + tmpname[4] = '0' + i; + // find unused register at this fragment + while( regIndex < kMaxTempRegisters && mapFragmentRegister[fi][regIndex] != 0 ) + ++regIndex; + assert(regIndex < kMaxTempRegisters); + if( regIndex > 9 ) { + regname2[1] = '1'; + regname2[2] = '0' + (regIndex-10); + replace_string(text, tmpname, regname2); + } else { + regname[1] = '0' + regIndex; + replace_string(text, tmpname, regname); + } + ++regIndex; + } + + // parameter + if( param >= 0 ) { + std::string paramString("0"); + assert(param<=9); + paramString[0] = '0'+param; + replace_string(text, "$PARAM", paramString); + } + + // texture matrix parameters + if( frag.options & kOptionHasTexMatrix ) { + std::string tmpstring("$TMPARAM0"); + std::string paramString("c00"); + for( int i = 0; i < 4; ++i ) { + assert(i<=9); + tmpstring[8] = '0' + i; + int constant = kConstantLocations[kConstMatrixTexture] + param*4 + i; + paramString[1] = '0' + constant/10; + paramString[2] = '0' + constant%10; + replace_string(text, tmpstring, paramString); + } + } + + output += text; + } + + + usedConstants = usedConstantsMask; + + // checks + + // should be no '$' left + assert( output.find('$') == std::string::npos ); + + // debug info + //char buffer[1000]; + //_snprintf_s( buffer, 1000, "Fragments: %i SavedRegs: %i\n", m_FragmentCount, maxTemps ); + //debug += buffer; + //for( size_t i = 0; i < savedRegisters.size(); ++i ) { + // _snprintf_s( buffer, 1000, " saved %s [%i..%i] r%i\n", savedRegisters[i].name.c_str(), savedRegisters[i].firstUse, savedRegisters[i].lastUse, savedRegisters[i].regIndex ); + // debug += buffer; + //} +} -- cgit v1.1-26-g67d0