You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1199 lines
38 KiB
C++
1199 lines
38 KiB
C++
/*
|
|
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
|
* its licensors.
|
|
*
|
|
* For complete copyright and license terms please see the LICENSE at the root of this
|
|
* distribution (the "License"). All use of this software is governed by the License,
|
|
* or, if provided, by the license below or the license accompanying this file. Do not
|
|
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
*
|
|
*/
|
|
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
|
|
|
#include "RenderDll_precompiled.h"
|
|
#include "I3DEngine.h"
|
|
#include "CryHeaders.h"
|
|
#include "../Common/Shaders/RemoteCompiler.h"
|
|
#include <MainThreadRenderRequestBus.h>
|
|
#include <Pak/CryPakUtils.h>
|
|
|
|
#include <IMaterial.h>
|
|
|
|
#ifndef NULL_RENDERER
|
|
#include "DriverD3D.h"
|
|
#endif
|
|
|
|
#if defined(WIN32) || defined(WIN64)
|
|
#include <direct.h>
|
|
#include <io.h>
|
|
#endif
|
|
|
|
extern char* gShObjectNotFound;
|
|
|
|
//=================================================================================================
|
|
|
|
bool CShader::Reload(int nFlags, const char* szShaderName)
|
|
{
|
|
CShader* pShaderGen = NULL;
|
|
if (m_ShaderGenParams)
|
|
{
|
|
pShaderGen = this;
|
|
}
|
|
else
|
|
if (m_pGenShader)
|
|
{
|
|
pShaderGen = m_pGenShader;
|
|
}
|
|
uint32 nFl = EF_RELOAD;
|
|
if (nFlags & FRO_FORCERELOAD)
|
|
{
|
|
nFl |= EF_FORCE_RELOAD;
|
|
}
|
|
if (pShaderGen && pShaderGen->m_DerivedShaders)
|
|
{
|
|
for (uint32 i = 0; i < pShaderGen->m_DerivedShaders->size(); i++)
|
|
{
|
|
CShader* pShader = (*pShaderGen->m_DerivedShaders)[i];
|
|
if (!pShader)
|
|
{
|
|
continue;
|
|
}
|
|
if (pShader->m_nRefreshFrame == gRenDev->m_cEF.m_nFrameForceReload)
|
|
{
|
|
continue;
|
|
}
|
|
pShader->m_nRefreshFrame = gRenDev->m_cEF.m_nFrameForceReload;
|
|
|
|
gRenDev->m_cEF.mfForName(szShaderName, pShader->m_Flags | nFl, NULL, pShader->m_nMaskGenFX);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
assert(!m_nMaskGenFX);
|
|
gRenDev->m_cEF.mfForName(szShaderName, m_Flags | nFl, NULL, m_nMaskGenFX);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CShaderMan::mfReloadShaderIncludes(const char* szPath, int nFlags)
|
|
{
|
|
assert(szPath);
|
|
|
|
bool bChanged = false;
|
|
char dirn[256];
|
|
cry_strcpy(dirn, szPath);
|
|
cry_strcat(dirn, "*");
|
|
AZ::IO::ArchiveFileIterator handle = gEnv->pCryPak->FindFirst (dirn);
|
|
if (handle)
|
|
{
|
|
do
|
|
{
|
|
if (handle.m_filename.front() == '.')
|
|
{
|
|
continue;
|
|
}
|
|
if ((handle.m_fileDesc.nAttrib & AZ::IO::FileDesc::Attribute::Subdirectory) == AZ::IO::FileDesc::Attribute::Subdirectory)
|
|
{
|
|
char ddd[256];
|
|
azsnprintf(ddd, AZ_ARRAY_SIZE(ddd), "%s%.*s/", szPath, aznumeric_cast<int>(handle.m_filename.size()), handle.m_filename.data());
|
|
|
|
bChanged = mfReloadShaderIncludes(ddd, nFlags);
|
|
continue;
|
|
}
|
|
char nmf[256];
|
|
cry_strcpy(nmf, szPath);
|
|
cry_strcat(nmf, handle.m_filename.data());
|
|
int len = strlen(nmf) - 1;
|
|
while (len >= 0 && nmf[len] != '.')
|
|
{
|
|
len--;
|
|
}
|
|
if (len <= 0)
|
|
{
|
|
continue;
|
|
}
|
|
if (!azstricmp(&nmf[len], ".cfi"))
|
|
{
|
|
fpStripExtension(handle.m_filename.data(), nmf);
|
|
bool bCh = false;
|
|
m_Bin.GetBinShader(nmf, true, 0, &bCh);
|
|
if (bCh)
|
|
{
|
|
bChanged = true;
|
|
mfReloadFile(szPath, handle.m_filename.data(), nFlags);
|
|
}
|
|
}
|
|
} while (handle = gEnv->pCryPak->FindNext(handle));
|
|
|
|
gEnv->pCryPak->FindClose (handle);
|
|
}
|
|
return bChanged;
|
|
}
|
|
|
|
bool CShaderMan::mfReloadAllShaders([[maybe_unused]] int nFlags, [[maybe_unused]] uint32 nFlagsHW)
|
|
{
|
|
bool bState = true;
|
|
m_nFrameForceReload++;
|
|
|
|
gRenDev->FlushRTCommands(true, true, true);
|
|
m_Bin.InvalidateCache();
|
|
CHWShader::mfFlushPendedShadersWait(-1);
|
|
|
|
#ifndef NULL_RENDERER
|
|
// Ensure all shaders are unbound before forcing a reload of all shaders
|
|
gRenDev->m_pRT->RC_UnbindResources();
|
|
if (!gRenDev->IsShaderCacheGenMode())
|
|
{
|
|
gRenDev->m_pRT->RC_ResetToDefault();
|
|
}
|
|
gRenDev->FlushRTCommands(true, true, true);
|
|
|
|
CDebugAllowFileAccess ignoreInvalidFileAccess;
|
|
|
|
// Check include changing
|
|
if IsCVarConstAccess(constexpr) (!CRenderer::CV_r_shadersignoreincludeschanging)
|
|
{
|
|
mfReloadShaderIncludes(m_ShadersPath.c_str(), nFlags);
|
|
}
|
|
CCryNameTSCRC Name = CShader::mfGetClassName();
|
|
SResourceContainer* pRL = CBaseResource::GetResourcesForClass(Name);
|
|
if (pRL)
|
|
{
|
|
ResourcesMapItor itor;
|
|
for (itor = pRL->m_RMap.begin(); itor != pRL->m_RMap.end(); itor++)
|
|
{
|
|
CShader* pS = (CShader*)itor->second;
|
|
if (!pS)
|
|
{
|
|
continue;
|
|
}
|
|
if (nFlagsHW)
|
|
{
|
|
if (!pS->m_pGenShader)
|
|
{
|
|
continue;
|
|
}
|
|
SShaderGen* pGen = pS->m_pGenShader->m_ShaderGenParams;
|
|
assert(pGen);
|
|
if (!pGen)
|
|
{
|
|
continue;
|
|
}
|
|
uint32 i;
|
|
SShaderGenBit* pBit;
|
|
for (i = 0; i < pGen->m_BitMask.Num(); i++)
|
|
{
|
|
pBit = pGen->m_BitMask[i];
|
|
if ((pBit->m_nDependencySet | pBit->m_nDependencyReset) & nFlagsHW)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (i == pGen->m_BitMask.Num())
|
|
{
|
|
continue;
|
|
}
|
|
pS->Reload(nFlags | FRO_FORCERELOAD, pS->GetName());
|
|
}
|
|
else
|
|
{
|
|
char name[256];
|
|
sprintf_s(name, "%sCryFX/%s.cfx", m_ShadersPath.c_str(), pS->GetName());
|
|
AZ::IO::HandleType fileHandle = gEnv->pCryPak->FOpen(name, "rb");
|
|
if (fileHandle != AZ::IO::InvalidHandle)
|
|
{
|
|
uint32 nSourceCRC32 = gEnv->pCryPak->ComputeCRC(name);
|
|
gEnv->pCryPak->FClose(fileHandle);
|
|
if ((nFlags & FRO_FORCERELOAD) || nSourceCRC32 != pS->m_SourceCRC32)
|
|
{
|
|
pS->m_SourceCRC32 = nSourceCRC32;
|
|
pS->Reload(nFlags, pS->GetName());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//Force all PSOs to be rebuilt
|
|
CDeviceObjectFactory::GetInstance().InvalidatePSOCache();
|
|
//Tell the graphics pipeline to reset and throw out existing PSOs since they're now invalid
|
|
gcpRendD3D->GetGraphicsPipeline().Reset();
|
|
|
|
#endif
|
|
|
|
gRenDev->FlushRTCommands(true, true, true);
|
|
CHWShader::mfFlushPendedShadersWait(-1);
|
|
|
|
return bState;
|
|
}
|
|
|
|
// Recursively iterate through all the shaders included by pBin to see if any match szShaderName
|
|
// Returns true if there is a match, meaning pBin is affected by szShaderName.
|
|
static bool sCheckAffecting_r(SShaderBin* pBin, const char* szShaderName)
|
|
{
|
|
pBin->Lock();
|
|
int nTok = 0;
|
|
// Check first level
|
|
while (nTok >= 0)
|
|
{
|
|
// For each include that is found, check to see if it matches szShaderName
|
|
nTok = CParserBin::FindToken(nTok, pBin->m_Tokens.size() - 1, &pBin->m_Tokens[0], eT_include);
|
|
if (nTok >= 0)
|
|
{
|
|
// If an include was found, check to see if it matches szShaderName
|
|
nTok++;
|
|
uint32 nTokName = pBin->m_Tokens[nTok];
|
|
const char* szNameInc = CParserBin::GetString(nTokName, pBin->m_TokenTable);
|
|
if (!azstricmp(szNameInc, szShaderName))
|
|
{
|
|
// If szShaderName matches the included shader, then pBin is affected by that shader
|
|
// Since nTok is >= 0, breaking here will result in returning 'true'
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// Check each of the included shaders recursively
|
|
if (nTok < 0)
|
|
{
|
|
nTok = 0;
|
|
while (nTok >= 0)
|
|
{
|
|
nTok = CParserBin::FindToken(nTok, pBin->m_Tokens.size() - 1, &pBin->m_Tokens[0], eT_include);
|
|
if (nTok >= 0)
|
|
{
|
|
// For each include that is found, check to see if it matches szShaderName
|
|
nTok++;
|
|
uint32 nTokName = pBin->m_Tokens[nTok];
|
|
const char* szNameInc = CParserBin::GetString(nTokName, pBin->m_TokenTable);
|
|
if (!azstricmp(szNameInc, szShaderName))
|
|
{
|
|
// If szShaderName matches the included shader, then pBin is affected by that shader
|
|
// Since nTok is >= 0, breaking here will result in returning 'true'
|
|
break;
|
|
}
|
|
SShaderBin* pBinIncl = gRenDev->m_cEF.m_Bin.GetBinShader(szNameInc, true, 0);
|
|
if (!pBinIncl)
|
|
{
|
|
AZ_Assert(false, "Error attempting to load shader %s while checking all the shaders included by %s.", szNameInc, pBin->m_szName);
|
|
break;
|
|
}
|
|
// Since the included file did not match szShaderName, recursively check all of its included shaders to see if they match
|
|
bool bAffect = sCheckAffecting_r(pBinIncl, szShaderName);
|
|
if (bAffect)
|
|
{
|
|
// One of the included shaders matched szShaderName
|
|
// Since nTok is >= 0, breaking here will result in returning 'true'
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
pBin->Unlock();
|
|
// Return true if a shader was found that matched szShaderName, false otherwise
|
|
return (nTok >= 0);
|
|
}
|
|
|
|
bool CShaderMan::mfReloadFile([[maybe_unused]] const char* szPath, const char* szName, int nFlags)
|
|
{
|
|
CHWShader::mfFlushPendedShadersWait(-1);
|
|
|
|
m_nFrameForceReload++;
|
|
|
|
const char* szExt = fpGetExtension(szName);
|
|
if (!azstricmp(szExt, ".cfx"))
|
|
{
|
|
m_bReload = true;
|
|
char szShaderName[256];
|
|
cry_strcpy(szShaderName, szName, (size_t)(szExt - szName));
|
|
azstrlwr(szShaderName, AZ_ARRAY_SIZE(szShaderName));
|
|
|
|
// Check if this shader already loaded
|
|
CBaseResource* pBR = CBaseResource::GetResource(CShader::mfGetClassName(), szShaderName, false);
|
|
if (pBR)
|
|
{
|
|
CShader* pShader = (CShader*)pBR;
|
|
pShader->Reload(nFlags, szShaderName);
|
|
}
|
|
m_bReload = false;
|
|
}
|
|
else
|
|
if (!azstricmp(szExt, ".cfi"))
|
|
{
|
|
CCryNameTSCRC Name = CShader::mfGetClassName();
|
|
SResourceContainer* pRL = CBaseResource::GetResourcesForClass(Name);
|
|
if (pRL)
|
|
{
|
|
m_bReload = true;
|
|
char szShaderName[256];
|
|
cry_strcpy(szShaderName, szName, (size_t)(szExt - szName));
|
|
azstrlwr(szShaderName, AZ_ARRAY_SIZE(szShaderName));
|
|
SShaderBin* pBin = m_Bin.GetBinShader(szShaderName, true, 0);
|
|
bool bAffect = false;
|
|
|
|
// Since this is a .cfi file, iterate through the existing resources to see if any of them are including it and consequently must be re-loaded
|
|
ResourcesMapItor itor;
|
|
for (itor = pRL->m_RMap.begin(); itor != pRL->m_RMap.end(); itor++)
|
|
{
|
|
CShader* sh = (CShader*)itor->second;
|
|
if (!sh || !sh->GetName()[0])
|
|
{
|
|
continue;
|
|
}
|
|
pBin = m_Bin.GetBinShader(sh->GetName(), false, 0);
|
|
if (!pBin)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Recursively check to see if sh is affected by the .cfi that is being reloaded
|
|
bAffect = sCheckAffecting_r(pBin, szShaderName);
|
|
if (bAffect)
|
|
{
|
|
// If sh is affected, it also needs to be reloaded
|
|
sh->Reload(nFlags | FRO_FORCERELOAD, sh->GetName());
|
|
}
|
|
}
|
|
}
|
|
m_bReload = false;
|
|
}
|
|
|
|
|
|
return false;
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
void CShaderMan::mfFillGenMacroses(SShaderGen* shG, TArray<char>& buf, uint64 nMaskGen)
|
|
{
|
|
uint32 i;
|
|
|
|
if (!nMaskGen || !shG)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < shG->m_BitMask.Num(); i++)
|
|
{
|
|
if (shG->m_BitMask[i]->m_Mask & nMaskGen)
|
|
{
|
|
char macro[256];
|
|
#if defined(__GNUC__)
|
|
sprintf_s(macro, "#define %s 0x%llx\n", shG->m_BitMask[i]->m_ParamName.c_str(), (unsigned long long)shG->m_BitMask[i]->m_Mask);
|
|
#else
|
|
sprintf_s(macro, "#define %s 0x%I64x\n", shG->m_BitMask[i]->m_ParamName.c_str(), (uint64)shG->m_BitMask[i]->m_Mask);
|
|
#endif
|
|
int size = strlen(macro);
|
|
int nOffs = buf.Num();
|
|
buf.Grow(size);
|
|
memcpy(&buf[nOffs], macro, size);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
bool CShaderMan::mfModifyGenFlags(CShader* efGen, const CShaderResources* pRes, uint64& nMaskGen, uint64& nMaskGenH)
|
|
{
|
|
if (!efGen || !efGen->m_ShaderGenParams)
|
|
{
|
|
return false;
|
|
}
|
|
uint64 nAndMaskHW = -1;
|
|
uint64 nMaskGenHW = 0;
|
|
|
|
// Remove non-used flags first
|
|
unsigned int i = 0;
|
|
SShaderGen* pGen = efGen->m_ShaderGenParams;
|
|
int j;
|
|
|
|
//char _debug_[256];
|
|
//sprintf_s( _debug_, "curr shadergen %I64x %I64x\n", nMaskGen, nMaskGenH);
|
|
//OutputDebugString(_debug_);
|
|
|
|
if (nMaskGen)
|
|
{
|
|
for (j = 0; j < 64; j++)
|
|
{
|
|
uint64 nMask = (((uint64)1) << j);
|
|
if (nMaskGen & nMask)
|
|
{
|
|
for (i = 0; i < pGen->m_BitMask.Num(); i++)
|
|
{
|
|
SShaderGenBit* pBit = pGen->m_BitMask[i];
|
|
if (pBit->m_Mask & nMask)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == pGen->m_BitMask.Num())
|
|
{
|
|
nMaskGen &= ~nMask;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < pGen->m_BitMask.Num(); i++)
|
|
{
|
|
SShaderGenBit* pBit = pGen->m_BitMask[i];
|
|
if (!pBit || (!pBit->m_nDependencySet && !pBit->m_nDependencyReset))
|
|
{
|
|
continue;
|
|
}
|
|
if (pRes)
|
|
{
|
|
if (pBit->m_nDependencySet & SHGD_TEX_NORMALS)
|
|
{
|
|
if (pRes->TextureSlotExists(EFTT_NORMALS))
|
|
{
|
|
nMaskGen |= pBit->m_Mask;
|
|
}
|
|
}
|
|
if (pBit->m_nDependencyReset & SHGD_TEX_NORMALS)
|
|
{
|
|
if (!pRes->TextureSlotExists(EFTT_NORMALS))
|
|
{
|
|
nMaskGen &= ~pBit->m_Mask;
|
|
}
|
|
}
|
|
if (pBit->m_nDependencySet & SHGD_TEX_HEIGHT)
|
|
{
|
|
if (pRes->TextureSlotExists(EFTT_HEIGHT))
|
|
{
|
|
nMaskGen |= pBit->m_Mask;
|
|
}
|
|
}
|
|
if (pBit->m_nDependencyReset & SHGD_TEX_HEIGHT)
|
|
{
|
|
if (!pRes->TextureSlotExists(EFTT_HEIGHT))
|
|
{
|
|
nMaskGen &= ~pBit->m_Mask;
|
|
}
|
|
}
|
|
|
|
if (pBit->m_nDependencySet & SHGD_TEX_DETAIL)
|
|
{
|
|
if (pRes->TextureSlotExists(EFTT_DETAIL_OVERLAY))
|
|
{
|
|
nMaskGen |= pBit->m_Mask;
|
|
}
|
|
}
|
|
if (pBit->m_nDependencyReset & SHGD_TEX_DETAIL)
|
|
{
|
|
if (!pRes->TextureSlotExists(EFTT_DETAIL_OVERLAY))
|
|
{
|
|
nMaskGen &= ~pBit->m_Mask;
|
|
}
|
|
}
|
|
|
|
if (pBit->m_nDependencySet & SHGD_TEX_SECOND_SMOOTHNESS)
|
|
{
|
|
if (pRes->TextureSlotExists(EFTT_SECOND_SMOOTHNESS))
|
|
{
|
|
nMaskGen |= pBit->m_Mask;
|
|
}
|
|
}
|
|
if (pBit->m_nDependencyReset & SHGD_TEX_SECOND_SMOOTHNESS)
|
|
{
|
|
if (!pRes->TextureSlotExists(EFTT_SECOND_SMOOTHNESS))
|
|
{
|
|
nMaskGen &= ~pBit->m_Mask;
|
|
}
|
|
}
|
|
|
|
if (pBit->m_nDependencySet & SHGD_TEX_SPECULAR)
|
|
{
|
|
if (pRes->TextureSlotExists(EFTT_SPECULAR))
|
|
{
|
|
nMaskGen |= pBit->m_Mask;
|
|
}
|
|
}
|
|
if (pBit->m_nDependencyReset & SHGD_TEX_SPECULAR)
|
|
{
|
|
if (!pRes->TextureSlotExists(EFTT_SPECULAR))
|
|
{
|
|
nMaskGen &= ~pBit->m_Mask;
|
|
}
|
|
}
|
|
|
|
if (pBit->m_nDependencySet & SHGD_TEX_ENVCM)
|
|
{
|
|
if (pRes->TextureSlotExists(EFTT_ENV))
|
|
{
|
|
nMaskGen |= pBit->m_Mask;
|
|
}
|
|
}
|
|
if (pBit->m_nDependencyReset & SHGD_TEX_ENVCM)
|
|
{
|
|
if (!pRes->TextureSlotExists(EFTT_ENV))
|
|
{
|
|
nMaskGen &= ~pBit->m_Mask;
|
|
}
|
|
}
|
|
|
|
if (pBit->m_nDependencySet & SHGD_TEX_SUBSURFACE)
|
|
{
|
|
if (pRes->TextureSlotExists(EFTT_SUBSURFACE))
|
|
{
|
|
nMaskGen |= pBit->m_Mask;
|
|
}
|
|
}
|
|
if (pBit->m_nDependencyReset & SHGD_TEX_SUBSURFACE)
|
|
{
|
|
if (!pRes->TextureSlotExists(EFTT_SUBSURFACE))
|
|
{
|
|
nMaskGen &= ~pBit->m_Mask;
|
|
}
|
|
}
|
|
|
|
if (pBit->m_nDependencySet & SHGD_TEX_DECAL)
|
|
{
|
|
if (pRes->TextureSlotExists(EFTT_DECAL_OVERLAY))
|
|
{
|
|
nMaskGen |= pBit->m_Mask;
|
|
}
|
|
}
|
|
if (pBit->m_nDependencyReset & SHGD_TEX_DECAL)
|
|
{
|
|
if (!pRes->TextureSlotExists(EFTT_DECAL_OVERLAY))
|
|
{
|
|
nMaskGen &= ~pBit->m_Mask;
|
|
}
|
|
}
|
|
|
|
if (pBit->m_nDependencySet & SHGD_TEX_CUSTOM)
|
|
{
|
|
if (pRes->TextureSlotExists(EFTT_CUSTOM))
|
|
{
|
|
nMaskGen |= pBit->m_Mask;
|
|
}
|
|
}
|
|
if (pBit->m_nDependencyReset & SHGD_TEX_CUSTOM)
|
|
{
|
|
if (!pRes->TextureSlotExists(EFTT_CUSTOM))
|
|
{
|
|
nMaskGen &= ~pBit->m_Mask;
|
|
}
|
|
}
|
|
|
|
if (pBit->m_nDependencySet & SHGD_TEX_CUSTOM_SECONDARY)
|
|
{
|
|
if (pRes->TextureSlotExists(EFTT_CUSTOM_SECONDARY))
|
|
{
|
|
nMaskGen |= pBit->m_Mask;
|
|
}
|
|
}
|
|
if (pBit->m_nDependencyReset & SHGD_TEX_CUSTOM_SECONDARY)
|
|
{
|
|
if (!pRes->TextureSlotExists(EFTT_CUSTOM_SECONDARY))
|
|
{
|
|
nMaskGen &= ~pBit->m_Mask;
|
|
}
|
|
}
|
|
|
|
if (pBit->m_nDependencySet & SHGD_TEX_SPECULAR_2)
|
|
{
|
|
if (pRes->TextureSlotExists(EFTT_SPECULAR_2))
|
|
{
|
|
nMaskGen |= pBit->m_Mask;
|
|
}
|
|
}
|
|
if (pBit->m_nDependencyReset & SHGD_TEX_SPECULAR_2)
|
|
{
|
|
if (!pRes->TextureSlotExists(EFTT_SPECULAR_2))
|
|
{
|
|
nMaskGen &= ~pBit->m_Mask;
|
|
}
|
|
}
|
|
|
|
if (pBit->m_nDependencySet & SHGD_TEX_EMITTANCE)
|
|
{
|
|
// If either the emittance or the decal overlay (emissive intensity) are used, SHGD_TEX_EMITTANCE should be defined
|
|
if (pRes->TextureSlotExists(EFTT_EMITTANCE) || pRes->TextureSlotExists(EFTT_DECAL_OVERLAY))
|
|
{
|
|
nMaskGen |= pBit->m_Mask;
|
|
}
|
|
}
|
|
if (pBit->m_nDependencyReset & SHGD_TEX_EMITTANCE)
|
|
{
|
|
// If neither the emittance nor the decal overlay (emissive intensity) are used, SHGD_TEX_EMITTANCE should not be defined
|
|
if (!pRes->TextureSlotExists(EFTT_EMITTANCE) && !pRes->TextureSlotExists(EFTT_DECAL_OVERLAY))
|
|
{
|
|
nMaskGen &= ~pBit->m_Mask;
|
|
}
|
|
}
|
|
|
|
|
|
if (pBit->m_nDependencySet & SHGD_TEX_OCC)
|
|
{
|
|
if (pRes->TextureSlotExists(EFTT_OCCLUSION))
|
|
{
|
|
nMaskGen |= pBit->m_Mask;
|
|
}
|
|
}
|
|
if (pBit->m_nDependencyReset & SHGD_TEX_OCC)
|
|
{
|
|
if (!pRes->TextureSlotExists(EFTT_OCCLUSION))
|
|
{
|
|
nMaskGen &= ~pBit->m_Mask;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Specific case - for user gl flags (eg: TEMP_TERRAIN, TEMP_VEGETATION)
|
|
// this is needed since we now use common shader global flags - else define inside shader.cfi will override correct shared value
|
|
if (pBit->m_nDependencySet & SHGD_USER_ENABLED)
|
|
{
|
|
nMaskGen |= pBit->m_Mask;
|
|
}
|
|
|
|
//if (m_nCombinationsProcess < 0 || m_bActivatePhase)
|
|
{
|
|
if (pBit->m_nDependencySet & SHGD_HW_BILINEARFP16)
|
|
{
|
|
nAndMaskHW &= ~pBit->m_Mask;
|
|
if (gRenDev->m_bDeviceSupportsFP16Filter)
|
|
{
|
|
nMaskGenHW |= pBit->m_Mask;
|
|
}
|
|
}
|
|
if (pBit->m_nDependencyReset & SHGD_HW_BILINEARFP16)
|
|
{
|
|
nAndMaskHW &= ~pBit->m_Mask;
|
|
if (!gRenDev->m_bDeviceSupportsFP16Filter)
|
|
{
|
|
nMaskGenHW &= ~pBit->m_Mask;
|
|
}
|
|
}
|
|
if (pBit->m_nDependencySet & SHGD_HW_SEPARATEFP16)
|
|
{
|
|
nAndMaskHW &= ~pBit->m_Mask;
|
|
if (gRenDev->m_bDeviceSupportsFP16Separate)
|
|
{
|
|
nMaskGenHW |= pBit->m_Mask;
|
|
}
|
|
}
|
|
if (pBit->m_nDependencyReset & SHGD_HW_SEPARATEFP16)
|
|
{
|
|
nAndMaskHW &= ~pBit->m_Mask;
|
|
if (!gRenDev->m_bDeviceSupportsFP16Separate)
|
|
{
|
|
nMaskGenHW &= ~pBit->m_Mask;
|
|
}
|
|
}
|
|
|
|
if (CParserBin::m_bShaderCacheGen)
|
|
{
|
|
// during shader cache gen, disable the special features in non D3D11 mode, and just accept
|
|
// the lines as they come in D3D11 mode
|
|
if (CParserBin::m_nPlatform != SF_D3D11 && CParserBin::m_nPlatform != SF_JASPER && CParserBin::m_nPlatform != SF_GL4 && CParserBin::m_nPlatform != SF_ORBIS)
|
|
{
|
|
if (pBit->m_nDependencySet & SHGD_HW_WATER_TESSELLATION)
|
|
{
|
|
nAndMaskHW &= ~pBit->m_Mask;
|
|
}
|
|
if (pBit->m_nDependencySet & SHGD_HW_SILHOUETTE_POM)
|
|
{
|
|
nAndMaskHW &= ~pBit->m_Mask;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PREFAST_SUPPRESS_WARNING(6326)
|
|
bool bWaterTessHW = CRenderer::CV_r_WaterTessellationHW != 0 && gRenDev->m_bDeviceSupportsTessellation;
|
|
if (pBit->m_nDependencySet & SHGD_HW_WATER_TESSELLATION)
|
|
{
|
|
nAndMaskHW &= ~pBit->m_Mask;
|
|
if (bWaterTessHW)
|
|
{
|
|
nMaskGenHW |= pBit->m_Mask;
|
|
}
|
|
}
|
|
if (pBit->m_nDependencyReset & SHGD_HW_WATER_TESSELLATION)
|
|
{
|
|
nAndMaskHW &= ~pBit->m_Mask;
|
|
if (!bWaterTessHW)
|
|
{
|
|
nMaskGenHW &= ~pBit->m_Mask;
|
|
}
|
|
}
|
|
|
|
PREFAST_SUPPRESS_WARNING(6326)
|
|
const bool useSilhouettePOM = CRenderer::CV_r_SilhouettePOM != 0;
|
|
if (pBit->m_nDependencySet & SHGD_HW_SILHOUETTE_POM)
|
|
{
|
|
nAndMaskHW &= ~pBit->m_Mask;
|
|
if (useSilhouettePOM)
|
|
{
|
|
nMaskGenHW |= pBit->m_Mask;
|
|
}
|
|
}
|
|
if (pBit->m_nDependencyReset & SHGD_HW_SILHOUETTE_POM)
|
|
{
|
|
nAndMaskHW &= ~pBit->m_Mask;
|
|
if (!useSilhouettePOM)
|
|
{
|
|
nMaskGenHW &= ~pBit->m_Mask;
|
|
}
|
|
}
|
|
|
|
PREFAST_SUPPRESS_WARNING(6326)
|
|
const bool useSAA = CRenderer::CV_r_SpecularAntialiasing != 0;
|
|
if (pBit->m_nDependencySet & SHGD_HW_SAA)
|
|
{
|
|
nAndMaskHW &= ~pBit->m_Mask;
|
|
if (useSAA)
|
|
{
|
|
nMaskGenHW |= pBit->m_Mask;
|
|
}
|
|
}
|
|
if (pBit->m_nDependencyReset & SHGD_HW_SAA)
|
|
{
|
|
nAndMaskHW &= ~pBit->m_Mask;
|
|
if (!useSAA)
|
|
{
|
|
nMaskGenHW &= ~pBit->m_Mask;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
nMaskGen &= nAndMaskHW;
|
|
nMaskGenH |= nMaskGenHW;
|
|
|
|
//sprintf_s( _debug_, "remap shadergen %I64x %I64x\n", nMaskGen, nMaskGenH);
|
|
//OutputDebugString(_debug_);
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CShaderMan::mfUpdateTechnik (SShaderItem& SI, CCryNameTSCRC& Name)
|
|
{
|
|
CShader* pSH = (CShader*)SI.m_pShader;
|
|
if (!(pSH->m_Flags & EF_LOADED))
|
|
{
|
|
return false;
|
|
}
|
|
uint32 i;
|
|
for (i = 0; i < pSH->m_HWTechniques.Num(); i++)
|
|
{
|
|
SShaderTechnique* pTech = pSH->m_HWTechniques[i];
|
|
//if (!(pTech->m_Flags & FHF_PUBLIC))
|
|
// continue;
|
|
if (pTech->m_NameCRC == Name)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (i == pSH->m_HWTechniques.Num())
|
|
{
|
|
SI.m_nTechnique = -1;
|
|
Warning("ERROR: CShaderMan::mfUpdateTechnik: couldn't find public technique for shader '%s'", pSH->GetName());
|
|
}
|
|
else
|
|
{
|
|
SI.m_nTechnique = i;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
SShaderItem CShaderMan::mfShaderItemForName (const char* nameEf, bool bShare, int flags, SInputShaderResources* Res, uint64 nMaskGen)
|
|
{
|
|
SShaderItem SI;
|
|
|
|
CShaderResources* pResource = NULL;
|
|
if (Res)
|
|
{
|
|
SI.m_pShaderResources = mfCreateShaderResources(Res, bShare);
|
|
pResource = (CShaderResources*)SI.m_pShaderResources;
|
|
pResource->m_ShaderParams = Res->m_ShaderParams;
|
|
m_pCurInputResources = Res;
|
|
}
|
|
|
|
string strShader = nameEf;
|
|
string strTechnique;
|
|
string strNew = strShader.SpanExcluding(".");
|
|
if (strNew.size() != strShader.size())
|
|
{
|
|
string second = string(&strShader.c_str()[strNew.size() + 1]);
|
|
strShader = strNew;
|
|
strTechnique = second;
|
|
}
|
|
|
|
if (SI.m_pShaderResources)
|
|
{
|
|
const threadID currentThread = CryGetCurrentThreadId();
|
|
threadID renderThread, mainThread;
|
|
|
|
gEnv->pRenderer->GetThreadIDs(mainThread, renderThread);
|
|
|
|
if (currentThread == mainThread)
|
|
{
|
|
mfRefreshResources((CShaderResources*)SI.m_pShaderResources);
|
|
}
|
|
else
|
|
{
|
|
AZStd::function<void()> runOnMainThread = [this, SI]()
|
|
{
|
|
mfRefreshResources((CShaderResources*)SI.m_pShaderResources);
|
|
};
|
|
|
|
EBUS_QUEUE_FUNCTION(AZ::MainThreadRenderRequestBus, runOnMainThread);
|
|
}
|
|
}
|
|
|
|
char shadName[256];
|
|
if (nameEf && nameEf[0])
|
|
{
|
|
cry_strcpy(shadName, strShader.c_str());
|
|
}
|
|
else
|
|
{
|
|
shadName[0] = 0;
|
|
}
|
|
|
|
SI.m_pShader = mfForName(shadName, flags, (CShaderResources*)SI.m_pShaderResources, nMaskGen);
|
|
|
|
// Get technique
|
|
if (strTechnique.size())
|
|
{
|
|
CCryNameTSCRC nTech(strTechnique.c_str());
|
|
if (!mfUpdateTechnik(SI, nTech))
|
|
{
|
|
SI.m_nTechnique = nTech.get(); // Postpone
|
|
}
|
|
}
|
|
SI.m_nPreprocessFlags = -1;
|
|
if (Res)
|
|
{
|
|
SI.m_pShaderResources = pResource;
|
|
if (pResource)
|
|
{
|
|
pResource->CreateModifiers(Res);
|
|
}
|
|
}
|
|
m_pCurInputResources = NULL;
|
|
return SI;
|
|
}
|
|
|
|
|
|
CShader* CShaderMan::mfForName (const char* nameSh, int flags, const CShaderResources* Res, uint64 nMaskGen)
|
|
{
|
|
CShader* ef, * efGen;
|
|
int id;
|
|
|
|
if (!nameSh || !nameSh[0])
|
|
{
|
|
Warning("Warning: CShaderMan::mfForName: NULL name\n");
|
|
s_DefaultShader->AddRef();
|
|
return s_DefaultShader;
|
|
}
|
|
|
|
char nameEf[256];
|
|
char nameNew[256];
|
|
char nameRes[256];
|
|
|
|
uint64 nMaskGenHW = 0;
|
|
uint64 maskGenStatic = m_staticFlags;
|
|
|
|
cry_strcpy(nameEf, nameSh);
|
|
|
|
cry_strcpy(nameRes, nameEf);
|
|
|
|
cry_strcat(nameRes, GetShaderLanguageResourceName());
|
|
|
|
if (maskGenStatic)
|
|
{
|
|
cry_strcat(nameRes, AZStd::string::format("(ST%llx)", maskGenStatic).c_str());
|
|
}
|
|
|
|
ef = NULL;
|
|
efGen = NULL;
|
|
|
|
// Check if this shader already loaded
|
|
CBaseResource* pBR = CBaseResource::GetResource(CShader::mfGetClassName(), nameRes, false);
|
|
bool bGenModified = false;
|
|
ef = (CShader*)pBR;
|
|
if (ef && ef->m_ShaderGenParams)
|
|
{
|
|
efGen = ef;
|
|
|
|
mfModifyGenFlags(efGen, Res, nMaskGen, nMaskGenHW);
|
|
bGenModified = true;
|
|
azsprintf(nameNew, "%s(%llx)", nameRes, nMaskGen);
|
|
pBR = CBaseResource::GetResource(CShader::mfGetClassName(), nameNew, false);
|
|
ef = (CShader*)pBR;
|
|
if (ef)
|
|
{
|
|
// Update the flags if HW specs changed
|
|
ef->m_nMaskGenFX = nMaskGen | nMaskGenHW;
|
|
assert(ef->m_pGenShader == efGen);
|
|
}
|
|
}
|
|
|
|
if (ef)
|
|
{
|
|
if (!(flags & EF_RELOAD))
|
|
{
|
|
ef->AddRef();
|
|
ef->m_Flags |= flags;
|
|
return ef;
|
|
}
|
|
else
|
|
{
|
|
ef->mfFree();
|
|
ef->m_Flags |= EF_RELOADED;
|
|
}
|
|
}
|
|
|
|
if (!efGen)
|
|
{
|
|
sprintf_s(nameNew, "Shaders/%s.ext", nameEf);
|
|
SShaderGen* pShGen = mfCreateShaderGenInfo(nameEf, false);
|
|
|
|
if (pShGen)
|
|
{
|
|
efGen = mfNewShader(nameRes);
|
|
efGen->SetRefCounter(0); // Hack: to avoid leaks in shader-gen's
|
|
efGen->m_NameShader = nameRes;
|
|
efGen->m_ShaderGenParams = pShGen;
|
|
}
|
|
}
|
|
if (!(flags & EF_RELOAD) || !ef)
|
|
{
|
|
if (efGen)
|
|
{
|
|
// Change gen flags based on dependency on resources info
|
|
if (!bGenModified)
|
|
{
|
|
//nMaskGen = gRenDev->EF_GetRemapedShaderMaskGen(nameSh, nMaskGen | nMaskGenHW);
|
|
mfModifyGenFlags(efGen, Res, nMaskGen, nMaskGenHW);
|
|
}
|
|
sprintf_s(nameNew, "%s(%llx)", nameRes, nMaskGen);
|
|
ef = mfNewShader(nameNew);
|
|
if (!ef)
|
|
{
|
|
return s_DefaultShader;
|
|
}
|
|
|
|
ef->m_nMaskGenFX = nMaskGen | nMaskGenHW;
|
|
ef->m_maskGenStatic = maskGenStatic;
|
|
ef->m_ShaderGenStaticParams = m_staticExt;
|
|
ef->m_pGenShader = efGen;
|
|
}
|
|
if (efGen && ef)
|
|
{
|
|
assert(efGen != ef);
|
|
if (!efGen->m_DerivedShaders)
|
|
{
|
|
efGen->m_DerivedShaders = new std::vector<CShader*>;
|
|
}
|
|
efGen->m_DerivedShaders->push_back(ef);
|
|
efGen->AddRef();
|
|
}
|
|
if (!ef)
|
|
{
|
|
ef = mfNewShader(nameRes);
|
|
if (!ef)
|
|
{
|
|
return s_DefaultShader;
|
|
}
|
|
|
|
ef->m_maskGenStatic = maskGenStatic;
|
|
ef->m_ShaderGenStaticParams = m_staticExt;
|
|
}
|
|
}
|
|
id = ef->GetID();
|
|
ef->m_NameShader = nameEf;
|
|
ef->m_NameShaderICRC = CCrc32::ComputeLowercase(nameEf);
|
|
|
|
#ifndef NULL_RENDERER
|
|
// Check for the new cryFX format
|
|
sprintf_s(nameNew, "%sCryFX/%s.cfx", m_ShadersPath.c_str(), nameEf);
|
|
ef->m_NameFile = nameNew;
|
|
ef->m_Flags |= flags;
|
|
gRenDev->m_pRT->RC_ParseShader(ef, nMaskGen | nMaskGenHW, flags, (CShaderResources*)Res);
|
|
return ef;
|
|
#endif
|
|
|
|
return ef;
|
|
}
|
|
|
|
void CShaderMan::CreateShaderExportRequestLine(const CShader* pSH, stack_string& exportString)
|
|
{
|
|
if (pSH)
|
|
{
|
|
exportString.Format("<%d>%s/%s(", SHADER_SERIALISE_VER, pSH->GetName(), pSH->GetName());
|
|
CreateShaderMaskGenString(pSH, exportString);
|
|
exportString += ")()(0)(0)(0)(VS)"; //fake normal request line format
|
|
}
|
|
}
|
|
|
|
void CShaderMan::CreateShaderMaskGenString(const CShader* pSH, stack_string& flagString)
|
|
{
|
|
uint64 glMask = pSH->m_nMaskGenFX;
|
|
|
|
if (glMask)
|
|
{
|
|
SShaderGenComb* c = NULL;
|
|
c = mfGetShaderGenInfo(pSH->GetName());
|
|
if (c && c->pGen && c->pGen->m_BitMask.Num())
|
|
{
|
|
bool bFirstTime = true;
|
|
SShaderGen* pG = c->pGen;
|
|
for (uint32 i = 0; i < 64; i++)
|
|
{
|
|
if (glMask & ((uint64)1 << i))
|
|
{
|
|
for (uint32 j = 0; j < pG->m_BitMask.Num(); j++)
|
|
{
|
|
SShaderGenBit* pBit = pG->m_BitMask[j];
|
|
if (pBit->m_Mask & (glMask & ((uint64)1 << i)))
|
|
{
|
|
if (bFirstTime)
|
|
{
|
|
bFirstTime = false;
|
|
}
|
|
else
|
|
{
|
|
flagString += "|";
|
|
}
|
|
|
|
flagString += pBit->m_ParamName.c_str();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CShaderMan::RT_ParseShader(CShader* pSH, uint64 nMaskGen, uint32 flags, [[maybe_unused]] CShaderResources* pRes)
|
|
{
|
|
CDebugAllowFileAccess ignoreInvalidFileAccess;
|
|
|
|
bool bSuccess = false;
|
|
#ifdef SHADERS_SERIALIZING
|
|
if (CRenderer::CV_r_shadersImport)
|
|
{
|
|
// Do not try and import fxb during cache generation
|
|
// PC would need to support import of console data
|
|
if (!gRenDev->IsShaderCacheGenMode())
|
|
{
|
|
ShaderImportResults importResults = ImportShader(pSH, m_Bin);
|
|
|
|
if (importResults == SHADER_IMPORT_SUCCESS)
|
|
{
|
|
bSuccess = true;
|
|
}
|
|
else
|
|
{
|
|
#ifdef SHADER_SERIALIZE_VERBOSE
|
|
{
|
|
stack_string flagString;
|
|
CreateShaderMaskGenString(pSH, flagString);
|
|
|
|
CryLog("[CShaderSerialize] Failed to import shader %s (0x%p) flags: 0x%llx 0x%x (%s)\n", pSH->GetName(), pSH, pSH->m_nMaskGenFX, pSH->m_nMDV, flagString.empty() ? "0" : flagString.c_str());
|
|
}
|
|
#endif
|
|
|
|
pSH->m_Flags |= EF_FAILED_IMPORT;
|
|
|
|
if (CRenderer::CV_r_shadersImport == 2)
|
|
{
|
|
// Do not fallback to the slow path unless we have a valid permutation in our lookup table (most optimal path)
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
// If importResults == SHADER_IMPORT_MISSING_ENTRY, then allow the fallback path if we have a valid .fxb file
|
|
// for this shader, but the current permutation is missing from the lookup table.
|
|
// This will fallback to the slow path to parse the .cfx for this shader permutation
|
|
if (importResults == SHADER_IMPORT_FAILURE)
|
|
{
|
|
// No .fxb was exported for this .cfx, so do not fallback.
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
if (!bSuccess)
|
|
{
|
|
#if !defined(SHADER_NO_SOURCES)
|
|
SShaderBin* pBin = m_Bin.GetBinShader(pSH->m_NameShader, false, 0);
|
|
if (pBin)
|
|
#endif
|
|
{
|
|
#if !defined(SHADER_NO_SOURCES)
|
|
if (flags & EF_FORCE_RELOAD)
|
|
{
|
|
uint32 nCRC32 = pBin->ComputeCRC();
|
|
if (nCRC32 != pBin->m_CRC32)
|
|
{
|
|
FXShaderBinValidCRCItor itor = gRenDev->m_cEF.m_Bin.m_BinValidCRCs.find(pBin->m_dwName);
|
|
if (itor == gRenDev->m_cEF.m_Bin.m_BinValidCRCs.end())
|
|
{
|
|
gRenDev->m_cEF.m_Bin.m_BinValidCRCs.insert(FXShaderBinValidCRCItor::value_type(pBin->m_dwName, false));
|
|
}
|
|
|
|
m_Bin.DeleteFromCache(pBin);
|
|
pBin = m_Bin.GetBinShader(pSH->m_NameShader, false, nCRC32);
|
|
}
|
|
}
|
|
#endif
|
|
bSuccess = m_Bin.ParseBinFX(pBin, pSH, nMaskGen);
|
|
#ifdef SHADERS_SERIALIZING
|
|
if (CRenderer::CV_r_shadersExport && gRenDev->IsShaderCacheGenMode())
|
|
{
|
|
//Shader compilation must be enabled for export, to allow reading the token table from the fxcbs in the USER dir
|
|
int oldAllowCompilation = CRenderer::CV_r_shadersAllowCompilation;
|
|
CRenderer::CV_r_shadersAllowCompilation = 1;
|
|
|
|
if (bSuccess)
|
|
{
|
|
// CheckFXBExists() used to only be queried here; however, that function will create the SResource under certain
|
|
// conditions if it does not exist and can erroneously cause ExportShader to not be called on the first time a shader .fxb
|
|
// is created.
|
|
if (!DoesSResourceExist(pSH) || !CheckFXBExists(pSH))
|
|
{
|
|
bool shaderExported = ExportShader(pSH, m_Bin);
|
|
#ifdef SHADER_SERIALIZE_VERBOSE
|
|
if (!shaderExported)
|
|
{
|
|
CryLog("[CShaderSerialize] ExportShader failed for shader %s\n", pSH->GetName());
|
|
}
|
|
#else
|
|
AZ_UNUSED(shaderExported);
|
|
#endif
|
|
}
|
|
#ifdef SHADER_SERIALIZE_VERBOSE
|
|
else
|
|
{
|
|
CryLog("[CShaderSerialize] Not exporting shader %s, it already exists\n", pSH->GetName());
|
|
}
|
|
#endif
|
|
}
|
|
|
|
CRenderer::CV_r_shadersAllowCompilation = oldAllowCompilation;
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
CryWarning(VALIDATOR_MODULE_RENDERER, VALIDATOR_ERROR, "[SHADERS] Failed to load shader '%s'!", pSH->m_NameShader.c_str());
|
|
pSH->m_Flags |= EF_NOTFOUND;
|
|
}
|
|
}
|
|
pSH->m_Flags |= EF_LOADED;
|
|
|
|
EBUS_QUEUE_EVENT(AZ::MaterialNotificationEventBus, OnShaderLoaded, pSH);
|
|
}
|