#if _MSC_VER >= 1700
#pragma warning( disable: 4005 )
#endif
#include <maya/MString.h>
#include <maya/MGlobal.h>
#include <maya/MFileObject.h>
#include <maya/MSceneMessage.h>
#include "dx11ShaderCompileHelper.h"
#include "dx11ShaderStrings.h"
#define WIN32_LEAN_AND_MEAN
#include <d3d11.h>
#if _MSC_VER < 1700
#include <d3dx11.h>
#endif
#include <d3dx11effect.h>
#if USE_DIRECTXSDK_D3DX11EFFECTS
#ifndef USE_BOOL
#define USE_BOOL // use BOOL instead of bool
#endif
#endif
#include <d3dcompiler.h>
#include <sys/stat.h>
#include <string.h>
#include <map>
#include <set>
#include <list>
namespace CDX11EffectCompileHelper
{
class CFileReferenceHelper
{
public:
MString resolveFileName(
const char* fileName)
const;
void setReferencePath(
MString fileName);
protected:
MString findFile(
const char* fileName)
const;
};
MString CFileReferenceHelper::resolveFileName(
const char* fileName)
const
{
int hasFile = file.
length() > 0;
if (hasFile == 0)
{
int idx = currFileName.
rindex(
'/');
if (idx == -1)
idx = currFileName.
rindex(
'\\');
if (idx != -1)
{
file = findFile(currFileName.
asChar());
}
}
{
MString expandedFileName(
MString(fileName).expandEnvironmentVariablesAndTilde());
file = findFile(expandedFileName.
asChar());
}
return file;
}
void CFileReferenceHelper::setReferencePath(
MString fileName)
{
int idx = fileName.
rindex(
'/');
if (idx == -1)
if (idx != -1)
{
}
}
MString CFileReferenceHelper::findFile(
const char* fileName)
const
{
struct stat statBuf;
const bool fullyQualified = name.index('/') == 0 || name.index('\\') == 0 || name.index(':') == 1;
if (fullyQualified && stat(name.asChar(), &statBuf) != -1)
{
return name;
}
char path[MAX_PATH];
MString searchPaths = getSearchPaths();
const char * psearchpath = searchPaths.
asChar();
if(name.index('/') == 0 || name.index('\\') == 0)
resolvedName = name.
substring(1, name.length() - 1);
else
resolvedName = name;
while (psearchpath < searchPaths.
asChar() + searchPaths.
length())
{
const char * endpath = strchr(psearchpath,';');
if (endpath)
{
strncpy(path,psearchpath, endpath - psearchpath);
path[endpath - psearchpath] = '\0';
}
else
{
strcpy(path,psearchpath);
}
psearchpath += strlen(path)+1;
bool fullPath = (path[0] == '/' || path[0] == '\\');
if (strlen(path) > 2)
{
fullPath = fullPath ||
(path[1] == ':' &&
(path[2] == '/' ||
path[2] == '\\'));
}
if(path[strlen(path) - 1] != '/')
file =
MString(path) +
"/" + resolvedName;
else
file =
MString(path) + resolvedName;
if (stat(file.
asChar(), &statBuf) != -1)
{
return file;
}
}
}
MString CFileReferenceHelper::getSearchPaths()
const
{
if ( status == MS::kSuccess)
{
searchPaths += workspace;
searchPaths += ";";
searchPaths += workspace;
searchPaths += "/renderData/shaders";
if(status== MS::kSuccess)
{
searchPaths += ";";
searchPaths += workspace;
searchPaths += shadersRelativePath;
}
}
if(referencePath.length() > 0)
{
{
searchPaths += ";";
}
searchPaths += referencePath;
}
static char * dx11ShaderRoot = getenv("DX11SHADER_ROOT");
if (dx11ShaderRoot)
{
{
searchPaths += ";";
}
searchPaths += dx11ShaderRoot;
searchPaths += ";";
searchPaths += dx11ShaderRoot;
searchPaths += "/shaders";
}
searchPaths += ";";
return searchPaths;
}
class CIncludeHelper: public ID3D10Include, public CFileReferenceHelper
{
public:
STDMETHOD(Open)(D3D10_INCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID *ppData, UINT *pBytes)
{
MString resolvedFileName = resolveFileName(pFileName);
FILE* file = fopen(resolvedFileName.
asChar(),
"rb");
if(file == NULL)
{
return E_FAIL;
}
fseek(file, 0, SEEK_END);
long size = ftell(file);
fseek(file, 0, SEEK_SET);
char *buffer = new char[size];
fread(buffer, 1, size, file);
fclose(file);
*ppData = buffer;
*pBytes = UINT(size);
return S_OK;
}
STDMETHOD(Close)(LPCVOID pData)
{
char* pChar = (char*)pData;
delete [] pChar;
return S_OK;
}
};
D3D10_SHADER_MACRO* getD3DMacros()
{
static D3D10_SHADER_MACRO macros[] = { { "DIRECT3D_VERSION", "0xb00" },
{ "_MAYA_", "1"},
{ "MAYA_DX11", "1"},
{ NULL, NULL } };
return macros;
}
unsigned int getShaderCompileFlags(bool useStrictness)
{
unsigned int flags = 0;
#ifdef _DEBUG
flags |= D3DCOMPILE_DEBUG;
flags |= D3DCOMPILE_SKIP_OPTIMIZATION;
#endif
if(useStrictness)
{
flags |= D3DCOMPILE_ENABLE_STRICTNESS;
}
else
{
flags |= D3DCOMPILE_ENABLE_BACKWARDS_COMPATIBILITY;
}
return flags;
}
bool effectHasHullShader(ID3DX11Effect* effect)
{
if(effect)
{
D3DX11_EFFECT_DESC effectDesc;
effect->GetDesc(&effectDesc);
for(unsigned int i = 0; i < effectDesc.Techniques; ++i)
{
ID3DX11EffectTechnique* technique = effect->GetTechniqueByIndex(i);
if(technique && technique->IsValid())
{
D3DX11_TECHNIQUE_DESC techniqueDesc;
technique->GetDesc(&techniqueDesc);
for(unsigned int j = 0;j < techniqueDesc.Passes;++j)
{
ID3DX11EffectPass* pass = technique->GetPassByIndex(j);
if(pass && pass->IsValid())
{
D3DX11_PASS_SHADER_DESC shaderDesc;
memset(&shaderDesc, 0, sizeof(D3DX11_PASS_SHADER_DESC));
HRESULT hr = pass->GetHullShaderDesc(&shaderDesc);
if(hr == S_OK && shaderDesc.pShaderVariable && shaderDesc.pShaderVariable->IsValid())
{
D3DX11_EFFECT_SHADER_DESC hullEffectDesc;
memset(&hullEffectDesc, 0, sizeof(D3DX11_EFFECT_SHADER_DESC));
hr = shaderDesc.pShaderVariable->GetShaderDesc(shaderDesc.ShaderIndex, &hullEffectDesc);
if (SUCCEEDED(hr) && hullEffectDesc.BytecodeLength)
{
ID3D11HullShader* pHullShader = NULL;
shaderDesc.pShaderVariable->GetHullShader(shaderDesc.ShaderIndex, &pHullShader);
if (pHullShader)
{
pHullShader->Release();
return true;
}
}
}
}
}
}
}
}
return false;
}
bool isValidEffectFile(
const MString& fileName,
bool& isCompiled)
{
if(idx > 0)
{
}
isCompiled = (extension == "fxo");
return (extension == "fx" || extension == "fxo");
}
void pushError(
const MString& fileName,
MString &errorLog, ID3DBlob* error)
{
char* pMessage = (error && error->GetBufferSize() > 0) ? (char*) error->GetBufferPointer() : NULL;
MString msg = dx11ShaderStrings::getString( dx11ShaderStrings::kErrorEffectCompile, args );
errorLog += msg;
}
void pushError(
MString &errorLog, ID3DBlob* error)
{
char* pMessage = (error && error->GetBufferSize() > 0) ? (char*) error->GetBufferPointer() : NULL;
MString msg = dx11ShaderStrings::getString( dx11ShaderStrings::kErrorEffectBuffer, args );
errorLog += msg;
}
time_t fileTimeStamp(
const MString& fileName)
{
struct stat statBuf;
if( stat(fileName.
asChar(), &statBuf) != 0 )
return 0;
return statBuf.st_mtime;
}
struct EffectKey
{
ID3D11Device* device;
time_t timeStamp;
};
bool operator< (const EffectKey& lhs, const EffectKey& rhs)
{
return (lhs.device < rhs.device) ||
(lhs.device == rhs.device && ( (lhs.timeStamp < rhs.timeStamp) ||
(lhs.timeStamp == rhs.timeStamp && strcmp(lhs.fileName.asChar(), rhs.fileName.asChar()) < 0) ) );
}
struct MStringSorter {
{
}
};
class EffectCollection
{
public:
ID3DX11Effect* acquire(dx11ShaderNode* node, ID3D11Device* device,
const MString& fileName);
ID3DX11Effect* acquire(dx11ShaderNode* node, ID3D11Device* device,
const MString& fileName, ID3DX11Effect* reference, ID3DX11Effect* source = NULL);
void release(dx11ShaderNode* node, ID3DX11Effect *effect,
const MString& fileName);
void getNodesUsingEffect(
const MString& fileName, ShaderNodeList &nodes)
const;
ID3DX11Effect* getReferenceEffectAndFileName(ID3DX11Effect *effect,
MString& fileName)
const;
private:
typedef std::map< EffectKey, ID3DX11Effect* > Key2ReferenceEffectMap;
Key2ReferenceEffectMap key2ReferenceEffectMap;
typedef std::pair< EffectKey, unsigned int > EffectKeyCountPair;
typedef std::map< ID3DX11Effect*, EffectKeyCountPair > ReferenceCountMap;
ReferenceCountMap referenceCountMap;
typedef std::map< ID3DX11Effect*, ID3DX11Effect* > Clone2ReferenceMap;
Clone2ReferenceMap clone2ReferenceMap;
typedef std::set< dx11ShaderNode* > NodeSet;
typedef std::map< MString, NodeSet, MStringSorter > Path2NodesMap;
Path2NodesMap path2NodesMap;
};
ID3DX11Effect* EffectCollection::acquire(dx11ShaderNode* node, ID3D11Device* device,
const MString& fileName)
{
ID3DX11Effect* effect = NULL;
EffectKey key = { device, fileName, fileTimeStamp(fileName) } ;
Key2ReferenceEffectMap::const_iterator it = key2ReferenceEffectMap.find(key);
if(it != key2ReferenceEffectMap.end())
{
ID3DX11Effect* reference = it->second;
effect = acquire(node, device, fileName, reference);
}
return effect;
}
ID3DX11Effect* EffectCollection::acquire(dx11ShaderNode* node, ID3D11Device* device,
const MString& fileName, ID3DX11Effect* reference, ID3DX11Effect* source )
{
path2NodesMap[fileName].insert(node);
if( reference == NULL )
return NULL;
if( source == NULL)
source = reference;
EffectKey key = { device, fileName, fileTimeStamp(fileName) } ;
{
Key2ReferenceEffectMap::const_iterator it = key2ReferenceEffectMap.find(key);
if(it == key2ReferenceEffectMap.end()) {
key2ReferenceEffectMap.insert( std::make_pair(key, reference) );
}
}
ID3DX11Effect* effect = NULL;
HRESULT hr = source->CloneEffect(0, &effect);
if( FAILED( hr ) || effect == NULL )
return NULL;
{
ReferenceCountMap::iterator it = referenceCountMap.find(reference);
if(it == referenceCountMap.end())
{
referenceCountMap.insert( std::make_pair(reference, std::make_pair(key, 1) ) );
}
else
{
++(it->second.second);
}
}
clone2ReferenceMap.insert( std::make_pair(effect, reference) );
return effect;
}
void EffectCollection::release(dx11ShaderNode* node, ID3DX11Effect *effect,
const MString& fileName)
{
if (effect)
{
Clone2ReferenceMap::iterator it = clone2ReferenceMap.find(effect);
if(it != clone2ReferenceMap.end())
{
ID3DX11Effect* reference = it->second;
ReferenceCountMap::iterator it2 = referenceCountMap.find(reference);
if(it2 != referenceCountMap.end())
{
if(it2->second.second == 1)
{
EffectKey &key = it2->second.first;
key2ReferenceEffectMap.erase(key);
referenceCountMap.erase(it2);
reference->Release();
}
else
{
--(it2->second.second);
}
}
clone2ReferenceMap.erase(it);
}
effect->Release();
}
path2NodesMap[fileName].erase(node);
if (path2NodesMap[fileName].empty())
path2NodesMap.erase(fileName);
}
ID3DX11Effect* EffectCollection::getReferenceEffectAndFileName(ID3DX11Effect *effect,
MString& fileName)
const
{
ID3DX11Effect* reference = NULL;
Clone2ReferenceMap::const_iterator it = clone2ReferenceMap.find(effect);
if(it != clone2ReferenceMap.end())
{
reference = it->second;
Key2ReferenceEffectMap::const_iterator it2 = key2ReferenceEffectMap.begin();
Key2ReferenceEffectMap::const_iterator it2End = key2ReferenceEffectMap.end();
for(; it2 != it2End; ++it2)
{
if(it2->second == reference)
{
fileName = it2->first.fileName;
break;
}
}
}
return reference;
}
void EffectCollection::getNodesUsingEffect(
const MString& fileName, ShaderNodeList &nodes)
const
{
Path2NodesMap::const_iterator itNodeSet = path2NodesMap.find(fileName);
if (itNodeSet != path2NodesMap.end())
{
const NodeSet& nodeSet = itNodeSet->second;
for (NodeSet::const_iterator itNode = nodeSet.begin(); itNode != nodeSet.end(); ++itNode)
{
nodes.push_back(*itNode);
}
}
}
static EffectCollection gEffectCollection;
class CompiledEffectCache {
public:
CompiledEffectCache();
~CompiledEffectCache();
static CompiledEffectCache* get();
ID3DX11Effect* find( ID3D11Device* device,
const MString& fileName );
void add(ID3D11Device* device,
const MString& fileName, ID3DX11Effect* effect );
private:
struct CacheData {
CacheData(ID3D11Device* device,
const MString& fileName, ID3DX11Effect* effect,
int firstAccess);
~CacheData();
ID3D11Device* mDevice;
time_t mTimeStamp;
ID3DX11Effect* mEffect;
int mLastAccess;
private:
CacheData(const CacheData&);
const CacheData& operator=(const CacheData&);
};
std::list<CacheData*> mCached;
int mAccessClock;
static const size_t kCacheSize = 8;
MCallbackId mExitCallback;
MCallbackId mFileNewCallback;
static void flushCache( void *data);
static CompiledEffectCache* sCachePtr;
};
CompiledEffectCache::CacheData::CacheData(ID3D11Device* device,
const MString& fileName, ID3DX11Effect* effect,
int firstAccess)
: mDevice(device)
, mFileName(fileName)
, mEffect(NULL)
, mLastAccess(firstAccess)
{
if (effect)
effect->CloneEffect(0, &mEffect);
mTimeStamp = fileTimeStamp(fileName);
}
CompiledEffectCache::CacheData::~CacheData()
{
if (mEffect)
mEffect->Release();
}
CompiledEffectCache::CompiledEffectCache() : mAccessClock(0) {
}
CompiledEffectCache::~CompiledEffectCache()
{
std::list<CacheData*>::iterator itCache = mCached.begin();
for ( ; itCache != mCached.end(); ++itCache )
{
delete *itCache;
}
}
void CompiledEffectCache::flushCache( void *data)
{
delete sCachePtr;
sCachePtr = NULL;
}
CompiledEffectCache* CompiledEffectCache::get()
{
if (!sCachePtr)
sCachePtr = new CompiledEffectCache();
return sCachePtr;
}
CompiledEffectCache* CompiledEffectCache::sCachePtr = NULL;
ID3DX11Effect* CompiledEffectCache::find( ID3D11Device* device,
const MString& fileName )
{
ID3DX11Effect* effect = NULL;
std::list<CacheData*>::iterator itCache = mCached.begin();
for ( ; itCache != mCached.end(); ++itCache )
{
CompiledEffectCache::CacheData *cacheItem(*itCache);
if ( cacheItem->mDevice == device &&
cacheItem->mFileName == fileName &&
cacheItem->mTimeStamp == fileTimeStamp(fileName) )
{
cacheItem->mLastAccess = ++mAccessClock;
cacheItem->mEffect->CloneEffect(0, &effect);
break;
}
}
return effect;
}
void CompiledEffectCache::add(ID3D11Device* device,
const MString& fileName, ID3DX11Effect* effect )
{
if (mCached.size() > kCacheSize)
{
std::list<CacheData*>::iterator itCache = mCached.begin();
std::list<CacheData*>::iterator oldestItem = itCache;
itCache++;
for ( ; itCache != mCached.end(); ++itCache )
{
if ( (*itCache)->mLastAccess < (*oldestItem)->mLastAccess )
oldestItem = itCache;
}
CacheData* oldData(*oldestItem);
mCached.erase(oldestItem);
delete oldData;
}
CacheData* newData(new CacheData(device, fileName, effect, ++mAccessClock));
if (newData->mEffect)
mCached.push_back( newData );
else
delete newData;
}
}
void CDX11EffectCompileHelper::releaseEffect(dx11ShaderNode* node, ID3DX11Effect* effect,
const MString& fileName)
{
MString resolvedFileName = CDX11EffectCompileHelper::resolveShaderFileName(fileName);
gEffectCollection.release(node, effect, resolvedFileName);
}
MString CDX11EffectCompileHelper::resolveShaderFileName(
const MString& fileName,
bool* fileExists)
{
MString resolvedFileName = fileName;
{
CIncludeHelper includeHelper;
resolvedFileName = includeHelper.resolveFileName(fileName.
asChar());
}
if( fileExists != NULL )
{
}
return resolvedFileName;
}
ID3DX11Effect* CDX11EffectCompileHelper::build(dx11ShaderNode* node, ID3D11Device* device,
const MString& fileName,
MString &errorLog,
bool useStrictness)
{
bool fileExits = false;
MString resolvedFileName = CDX11EffectCompileHelper::resolveShaderFileName(fileName, &fileExits);
if(fileExits == false)
{
MString msg = dx11ShaderStrings::getString( dx11ShaderStrings::kErrorFileNotFound, resolvedFileName );
errorLog += msg;
return NULL;
}
bool compiledEffect = false;
if( isValidEffectFile(resolvedFileName, compiledEffect) == false )
{
MString msg = dx11ShaderStrings::getString( dx11ShaderStrings::kErrorInvalidEffectFile, resolvedFileName );
errorLog += msg;
return NULL;
}
ID3DX11Effect *effect = gEffectCollection.acquire(node, device, resolvedFileName);
if( effect == NULL ) {
effect = CompiledEffectCache::get()->find(device, resolvedFileName);
if( effect == NULL ) {
{
args.
append(resolvedFileName);
MString msg = dx11ShaderStrings::getString( dx11ShaderStrings::kErrorAbsolutePathNotFound, args );
errorLog += msg;
}
CIncludeHelper includeHelper;
includeHelper.setReferencePath(resolvedFileName);
ID3DBlob *shader = NULL;
ID3DBlob *error = NULL;
HRESULT hr = S_FALSE;
if( compiledEffect )
{
FILE* file = fopen(resolvedFileName.
asChar(),
"rb");
if(file)
{
fseek(file, 0, SEEK_END);
long size = ftell(file);
fseek(file, 0, SEEK_SET);
hr = D3DCreateBlob(size, &shader);
if( SUCCEEDED( hr ) )
{
fread(shader->GetBufferPointer(), 1, size, file);
}
fclose(file);
}
}
else
{
unsigned int compileFlags = getShaderCompileFlags(useStrictness);
D3D10_SHADER_MACRO* macros = getD3DMacros();
#if _MSC_VER < 1700
hr = D3DX11CompileFromFile(resolvedFileName.
asChar(), macros, &includeHelper, NULL,
"fx_5_0", compileFlags, 0, NULL, &shader, &error, NULL);
#else
hr = D3DCompileFromFile(resolvedFileName.
asWChar(), macros, &includeHelper, NULL,
"fx_5_0", compileFlags, 0, &shader, &error);
#endif
}
if( FAILED( hr ) || shader == NULL )
{
pushError(fileName, errorLog, error);
}
else if( shader )
{
hr = D3DX11CreateEffectFromMemory(shader->GetBufferPointer(), shader->GetBufferSize(), 0, device, &effect);
if( FAILED( hr ) || effect == NULL )
{
pushError(fileName, errorLog, error);
}
}
if( shader ) {
shader->Release();
}
if( compiledEffect == false && useStrictness == false && effect != NULL && effectHasHullShader(effect) ) {
effect->Release();
effect = CDX11EffectCompileHelper::build(node, device, fileName, errorLog, true );
return effect;
}
CompiledEffectCache::get()->add(device, resolvedFileName, effect);
}
effect = gEffectCollection.acquire(node, device, resolvedFileName, effect);
}
return effect;
}
ID3DX11Effect* CDX11EffectCompileHelper::build(dx11ShaderNode* node, ID3D11Device* device,
const MString& fileName, ID3DX11Effect* effectSource,
MString &errorLog)
{
MString resolvedFileName = CDX11EffectCompileHelper::resolveShaderFileName(fileName);
ID3DX11Effect *effect = NULL;
ID3DX11Effect *reference = gEffectCollection.getReferenceEffectAndFileName(effectSource, referenceResolvedFileName);
if(reference != NULL && resolvedFileName == referenceResolvedFileName)
{
effect = gEffectCollection.acquire(node, device, resolvedFileName, reference, effectSource);
}
return effect;
}
ID3DX11Effect* CDX11EffectCompileHelper::build(dx11ShaderNode* node, ID3D11Device* device,
const void* buffer,
unsigned int dataSize,
MString &errorLog,
bool useStrictness)
{
unsigned int compileFlags = getShaderCompileFlags(useStrictness);
D3D10_SHADER_MACRO* macros = getD3DMacros();
CIncludeHelper includeHelper;
ID3DX11Effect *effect = NULL;
ID3DBlob *shader = NULL;
ID3DBlob *error = NULL;
HRESULT hr = S_FALSE;
#if _MSC_VER < 1700
hr = D3DX11CompileFromMemory((char*)buffer, dataSize, NULL, macros, &includeHelper, "", "fx_5_0", compileFlags, 0, NULL, &shader, &error, NULL);
#else
hr = D3DCompile((char*)buffer, dataSize, NULL, macros, &includeHelper, "", "fx_5_0", compileFlags, 0, &shader, &error);
#endif
if( FAILED( hr ) || shader == NULL )
{
pushError(errorLog, error);
}
else if( shader )
{
hr = D3DX11CreateEffectFromMemory(shader->GetBufferPointer(), shader->GetBufferSize(), 0, device, &effect);
if( FAILED( hr ) || effect == NULL )
{
pushError(errorLog, error);
}
}
if( shader ) {
shader->Release();
}
if( useStrictness == false && effect != NULL && effectHasHullShader(effect) ) {
effect->Release();
effect = CDX11EffectCompileHelper::build(node, device, buffer, dataSize, errorLog, true );
}
return effect;
}
void CDX11EffectCompileHelper::getNodesUsingEffect(
const MString& fileName, ShaderNodeList &nodes)
{
MString resolvedFileName = CDX11EffectCompileHelper::resolveShaderFileName(fileName);
gEffectCollection.getNodesUsingEffect(resolvedFileName, nodes);
}