123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2012 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- #include "platform/platform.h"
- #include "shaderGen/shaderGen.h"
- #include "shaderGen/conditionerFeature.h"
- #include "core/stream/fileStream.h"
- #include "shaderGen/featureMgr.h"
- #include "shaderGen/shaderOp.h"
- #include "gfx/gfxDevice.h"
- #include "core/memVolume.h"
- #include "core/module.h"
- #ifdef TORQUE_D3D11
- #include "shaderGen/HLSL/customFeatureHLSL.h"
- #endif
- #ifdef TORQUE_OPENGL
- #include "shaderGen/GLSL/customFeatureGLSL.h"
- #endif
- MODULE_BEGIN( ShaderGen )
- MODULE_INIT_BEFORE( GFX )
- MODULE_SHUTDOWN_AFTER( GFX )
- MODULE_INIT
- {
- ManagedSingleton< ShaderGen >::createSingleton();
- }
-
- MODULE_SHUTDOWN
- {
- ManagedSingleton< ShaderGen >::deleteSingleton();
- }
- MODULE_END;
- String ShaderGen::smCommonShaderPath("shaders/common");
- ShaderGen::ShaderGen()
- {
- mInit = false;
- GFXDevice::getDeviceEventSignal().notify(this, &ShaderGen::_handleGFXEvent);
- mOutput = NULL;
- }
- ShaderGen::~ShaderGen()
- {
- GFXDevice::getDeviceEventSignal().remove(this, &ShaderGen::_handleGFXEvent);
- _uninit();
- }
- void ShaderGen::registerInitDelegate(GFXAdapterType adapterType, ShaderGenInitDelegate& initDelegate)
- {
- mInitDelegates[(U32)adapterType] = initDelegate;
- }
- bool ShaderGen::_handleGFXEvent(GFXDevice::GFXDeviceEventType event)
- {
- switch (event)
- {
- case GFXDevice::deInit :
- initShaderGen();
- break;
- case GFXDevice::deDestroy :
- {
- flushProceduralShaders();
- }
- break;
- default :
- break;
- }
- return true;
- }
- void ShaderGen::initShaderGen()
- {
- if (mInit)
- return;
- const GFXAdapterType adapterType = GFX->getAdapterType();
- if (!mInitDelegates[adapterType])
- return;
- smCommonShaderPath = String(Con::getVariable("$Core::CommonShaderPath", "shaders/common"));
- mInitDelegates[adapterType](this);
- mFeatureInitSignal.trigger( adapterType );
- mInit = true;
- String shaderPath = Con::getVariable( "$shaderGen::cachePath");
- if (!shaderPath.equal( "shadergen:" ) && !shaderPath.isEmpty() )
- {
- // this is necessary, especially under Windows with UAC enabled
- if (!Torque::FS::VerifyWriteAccess(shaderPath))
- {
- // we don't have write access so enable the virtualized memory store
- Con::warnf("ShaderGen: Write permission unavailable, switching to virtualized memory storage");
- shaderPath.clear();
- }
- }
- if ( shaderPath.equal( "shadergen:" ) || shaderPath.isEmpty() )
- {
- // If we didn't get a path then we're gonna cache the shaders to
- // a virtualized memory file system.
- mMemFS = new Torque::Mem::MemFileSystem( "shadergen:/" );
- Torque::FS::Mount( "shadergen", mMemFS );
- }
- else
- Torque::FS::Mount( "shadergen", shaderPath + "/" );
- // Delete the auto-generated conditioner include file.
- Torque::FS::Remove( "shadergen:/" + ConditionerFeature::ConditionerIncludeFileName );
- }
- void ShaderGen::generateShader( const MaterialFeatureData &featureData,
- char *vertFile,
- char *pixFile,
- F32 *pixVersion,
- const GFXVertexFormat *vertexFormat,
- const char* cacheName,
- Vector<GFXShaderMacro> ¯os)
- {
- PROFILE_SCOPE( ShaderGen_GenerateShader );
- mFeatureData = featureData;
- mVertexFormat = vertexFormat;
- _uninit();
- _init();
- char vertShaderName[256];
- char pixShaderName[256];
- // Note: We use a postfix of _V/_P here so that it sorts the matching
- // vert and pixel shaders together when listed alphabetically.
- dSprintf( vertShaderName, sizeof(vertShaderName), "shadergen:/%s_V.%s", cacheName, mFileEnding.c_str() );
- dSprintf( pixShaderName, sizeof(pixShaderName), "shadergen:/%s_P.%s", cacheName, mFileEnding.c_str() );
-
- dStrcpy( vertFile, vertShaderName, 256 );
- dStrcpy( pixFile, pixShaderName, 256 );
-
- // this needs to change - need to optimize down to ps v.1.1
- *pixVersion = GFX->getPixelShaderVersion();
-
- if ( !Con::getBoolVariable( "ShaderGen::GenNewShaders", true ) )
- {
- // If we are not regenerating the shader we will return here.
- // But we must fill in the shader macros first!
- _processVertFeatures( macros, true );
- _processPixFeatures( macros, true );
- return;
- }
- // create vertex shader
- //------------------------
- FileStream* s = new FileStream();
- if(!s->open(vertShaderName, Torque::FS::File::Write ))
- {
- AssertFatal(false, "Failed to open Shader Stream" );
- return;
- }
- mOutput = new MultiLine;
- mInstancingFormat.clear();
- _processVertFeatures(macros);
- _printVertShader( *s );
- delete s;
-
- ((ShaderConnector*)mComponents[C_CONNECTOR])->reset();
- LangElement::deleteElements();
- // create pixel shader
- //------------------------
- s = new FileStream();
- if(!s->open(pixShaderName, Torque::FS::File::Write ))
- {
- AssertFatal(false, "Failed to open Shader Stream" );
- return;
- }
- mOutput = new MultiLine;
- _processPixFeatures(macros);
- _printPixShader( *s );
- delete s;
- LangElement::deleteElements();
- }
- void ShaderGen::_init()
- {
- _createComponents();
- }
- void ShaderGen::_uninit()
- {
- for( U32 i=0; i<mComponents.size(); i++ )
- {
- delete mComponents[i];
- mComponents[i] = NULL;
- }
- mComponents.setSize(0);
- LangElement::deleteElements();
- Var::reset();
- }
- void ShaderGen::_createComponents()
- {
- ShaderComponent* vertComp = mComponentFactory->createVertexInputConnector( *mVertexFormat );
- mComponents.push_back(vertComp);
- ShaderComponent* vertPixelCon = mComponentFactory->createVertexPixelConnector();
- mComponents.push_back(vertPixelCon);
- ShaderComponent* vertParamDef = mComponentFactory->createVertexParamsDef();
- mComponents.push_back(vertParamDef);
- ShaderComponent* pixParamDef = mComponentFactory->createPixelParamsDef();
- mComponents.push_back(pixParamDef);
- }
- //----------------------------------------------------------------------------
- // Process features
- //----------------------------------------------------------------------------
- void ShaderGen::_processVertFeatures( Vector<GFXShaderMacro> ¯os, bool macrosOnly )
- {
- const FeatureSet &features = mFeatureData.features;
- for( U32 i=0; i < features.getCount(); i++ )
- {
- S32 index;
- const FeatureType &type = features.getAt( i, &index );
- ShaderFeature* feature = FEATUREMGR->getByType( type );
- if ( feature )
- {
- feature->setProcessIndex( index );
- feature->processVertMacros( macros, mFeatureData );
- if ( macrosOnly )
- continue;
- feature->setInstancingFormat( &mInstancingFormat );
- feature->mVertexFormat = mVertexFormat;
- feature->processVert( mComponents, mFeatureData );
- String line;
- if ( index > -1 )
- line = String::ToString( " // %s %d\r\n", feature->getName().c_str(), index );
- else
- line = String::ToString( " // %s\r\n", feature->getName().c_str() );
- mOutput->addStatement( new GenOp( line ) );
- if ( feature->getOutput() )
- mOutput->addStatement( feature->getOutput() );
- feature->reset();
- mOutput->addStatement( new GenOp( " \r\n" ) );
- }
- }
- ShaderConnector *connect = dynamic_cast<ShaderConnector *>( mComponents[C_CONNECTOR] );
- connect->sortVars();
- }
- void ShaderGen::_processPixFeatures( Vector<GFXShaderMacro> ¯os, bool macrosOnly )
- {
- const FeatureSet &features = mFeatureData.features;
- for( U32 i=0; i < features.getCount(); i++ )
- {
- S32 index;
- const FeatureType &type = features.getAt( i, &index );
- ShaderFeature* feature = FEATUREMGR->getByType( type );
- if ( feature )
- {
- feature->setProcessIndex( index );
- feature->processPixMacros( macros, mFeatureData );
- if ( macrosOnly )
- continue;
- feature->setInstancingFormat( &mInstancingFormat );
- feature->processPix( mComponents, mFeatureData );
- String line;
- if ( index > -1 )
- line = String::ToString( " // %s %d\r\n", feature->getName().c_str(), index );
- else
- line = String::ToString( " // %s\r\n", feature->getName().c_str() );
- mOutput->addStatement( new GenOp( line ) );
- if ( feature->getOutput() )
- mOutput->addStatement( feature->getOutput() );
- feature->reset();
- mOutput->addStatement( new GenOp( " \r\n" ) );
- }
- }
-
- ShaderConnector *connect = dynamic_cast<ShaderConnector *>( mComponents[C_CONNECTOR] );
- connect->sortVars();
- }
- void ShaderGen::_printFeatureList(Stream &stream)
- {
- mPrinter->printLine(stream, "// Features:");
-
- const FeatureSet &features = mFeatureData.features;
- for( U32 i=0; i < features.getCount(); i++ )
- {
- S32 index;
- const FeatureType &type = features.getAt( i, &index );
- ShaderFeature* feature = FEATUREMGR->getByType( type );
- if ( feature )
- {
- String line;
- if ( index > -1 )
- line = String::ToString( "// %s %d", feature->getName().c_str(), index );
- else
- line = String::ToString( "// %s", feature->getName().c_str() );
- mPrinter->printLine( stream, line );
- }
- }
- mPrinter->printLine(stream, "");
- }
- void ShaderGen::_printDependencies(Stream &stream)
- {
- Vector<const ShaderDependency *> dependencies;
- for( U32 i=0; i < FEATUREMGR->getFeatureCount(); i++ )
- {
- const FeatureInfo &info = FEATUREMGR->getAt( i );
- if ( mFeatureData.features.hasFeature( *info.type ) )
- dependencies.merge( info.feature->getDependencies() );
- }
- // Do a quick loop removing any duplicate dependancies.
- for( U32 i=0; i < dependencies.size(); )
- {
- bool dup = false;
- for( U32 j=0; j < dependencies.size(); j++ )
- {
- if ( j != i &&
- *dependencies[i] == *dependencies[j] )
- {
- dup = true;
- break;
- }
- }
- if ( dup )
- dependencies.erase( i );
- else
- i++;
- }
- // Print dependencies
- if( dependencies.size() > 0 )
- {
- mPrinter->printLine(stream, "// Dependencies:");
- for( S32 i = 0; i < dependencies.size(); i++ )
- dependencies[i]->print( stream );
- mPrinter->printLine(stream, "");
- }
- }
- void ShaderGen::_printFeatures( Stream &stream )
- {
- mOutput->print( stream );
- }
- void ShaderGen::_printVertShader( Stream &stream )
- {
- mPrinter->printShaderHeader(stream);
- _printDependencies(stream); // TODO: Split into vert and pix dependencies?
- _printFeatureList(stream);
- // print out structures
- mComponents[C_VERT_STRUCT]->print( stream, true );
- mComponents[C_CONNECTOR]->print( stream, true );
- mPrinter->printMainComment(stream);
- mComponents[C_VERT_MAIN]->print( stream, true );
- mComponents[C_VERT_STRUCT]->printOnMain( stream, true );
- // print out the function
- _printFeatures( stream );
- mPrinter->printVertexShaderCloser(stream);
- }
- void ShaderGen::_printPixShader( Stream &stream )
- {
- mPrinter->printShaderHeader(stream);
- _printDependencies(stream); // TODO: Split into vert and pix dependencies?
- _printFeatureList(stream);
- mComponents[C_CONNECTOR]->print( stream, false );
- mPrinter->printPixelShaderOutputStruct(stream, mFeatureData);
- mPrinter->printMainComment(stream);
- mComponents[C_PIX_MAIN]->print( stream, false );
- mComponents[C_CONNECTOR]->printOnMain( stream, false );
- // print out the function
- _printFeatures( stream );
- mPrinter->printPixelShaderCloser(stream);
- }
- GFXShader* ShaderGen::getShader( const MaterialFeatureData &featureData, const GFXVertexFormat *vertexFormat, const Vector<GFXShaderMacro> *macros, const Vector<String> &samplers )
- {
- PROFILE_SCOPE( ShaderGen_GetShader );
- const FeatureSet &features = featureData.codify();
- // Build a description string from the features
- // and vertex format combination ( and macros ).
- String shaderDescription = vertexFormat->getDescription() + features.getDescription();
- if ( macros && !macros->empty() )
- {
- String macroStr;
- GFXShaderMacro::stringize( *macros, ¯oStr );
- shaderDescription += macroStr;
- }
- // Generate a single 64bit hash from the description string.
- //
- // Don't get paranoid! This has 1 in 18446744073709551616
- // chance for collision... it won't happen in this lifetime.
- //
- U64 hash = Torque::hash64( (const U8*)shaderDescription.c_str(), shaderDescription.length(), 0 );
- hash = convertHostToLEndian(hash);
- U32 high = (U32)( hash >> 32 );
- U32 low = (U32)( hash & 0x00000000FFFFFFFF );
- String cacheKey = String::ToString( "%x%x", high, low );
- // return shader if exists
- GFXShader *match = mProcShaders[cacheKey];
- if ( match )
- return match;
- // if not, then create it
- char vertFile[256];
- char pixFile[256];
- F32 pixVersion;
- Vector<GFXShaderMacro> shaderMacros;
- shaderMacros.push_back( GFXShaderMacro( "TORQUE_SHADERGEN" ) );
- if ( macros )
- shaderMacros.merge( *macros );
- generateShader( featureData, vertFile, pixFile, &pixVersion, vertexFormat, cacheKey, shaderMacros );
- GFXShader *shader = GFX->createShader();
- if (!shader->init(vertFile, pixFile, pixVersion, shaderMacros, samplers, &mInstancingFormat))
- {
- delete shader;
- return NULL;
- }
- mProcShaders[cacheKey] = shader;
- return shader;
- }
- void ShaderGen::flushProceduralShaders()
- {
- // The shaders are reference counted, so we
- // just need to clear the map.
- mProcShaders.clear();
- }
|