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.
2609 lines
78 KiB
C++
2609 lines
78 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 "RemoteCompiler.h"
|
|
#include "../RenderCapabilities.h"
|
|
|
|
#include <AzCore/PlatformId/PlatformId.h>
|
|
#include <AzFramework/API/ApplicationAPI.h>
|
|
#include <AzFramework/Archive/IArchive.h>
|
|
|
|
uint32 SShaderCombIdent::PostCreate()
|
|
{
|
|
FUNCTION_PROFILER_RENDER_FLAT
|
|
// using actual CRC is to expensive, so replace with cheaper version with
|
|
// has more changes of hits
|
|
//uint32 hash = CCrc32::Compute(&m_RTMask, sizeof(SShaderCombIdent)-sizeof(uint32));
|
|
const uint32* acBuffer = alias_cast<uint32*>(&m_RTMask);
|
|
int len = (sizeof(SShaderCombIdent) - sizeof(uint32)) / sizeof(uint32);
|
|
uint32 hash = 5381;
|
|
while (len--)
|
|
{
|
|
int c = *acBuffer++;
|
|
// hash = hash*33 + c
|
|
hash = ((hash << 5) + hash) + c;
|
|
}
|
|
|
|
m_nHash = hash;
|
|
m_MDVMask &= ~SF_PLATFORM;
|
|
return hash;
|
|
}
|
|
|
|
bool CShader::mfPrecache(SShaderCombination& cmb, bool bForce, bool bCompressedOnly, CShaderResources* pRes)
|
|
{
|
|
bool bRes = true;
|
|
|
|
if (!CRenderer::CV_r_shadersAllowCompilation && !bForce)
|
|
{
|
|
return bRes;
|
|
}
|
|
|
|
int nAsync = CRenderer::CV_r_shadersasynccompiling;
|
|
CRenderer::CV_r_shadersasynccompiling = 0;
|
|
|
|
uint32 i, j;
|
|
|
|
//is this required? Messing with the global render state?
|
|
//gRenDev->m_RP.m_pShader = this;
|
|
//gRenDev->m_RP.m_pCurTechnique = NULL;
|
|
|
|
for (i = 0; i < m_HWTechniques.Num(); i++)
|
|
{
|
|
SShaderTechnique* pTech = m_HWTechniques[i];
|
|
for (j = 0; j < pTech->m_Passes.Num(); j++)
|
|
{
|
|
SShaderPass& Pass = pTech->m_Passes[j];
|
|
SShaderCombination c = cmb;
|
|
gRenDev->m_RP.m_FlagsShader_MD = cmb.m_MDMask;
|
|
if (Pass.m_PShader)
|
|
{
|
|
bRes &= Pass.m_PShader->mfPrecache(cmb, bForce, false, bCompressedOnly, this, pRes);
|
|
}
|
|
cmb.m_MDMask = gRenDev->m_RP.m_FlagsShader_MD;
|
|
if (Pass.m_VShader)
|
|
{
|
|
bRes &= Pass.m_VShader->mfPrecache(cmb, bForce, false, bCompressedOnly, this, pRes);
|
|
}
|
|
cmb = c;
|
|
}
|
|
}
|
|
CRenderer::CV_r_shadersasynccompiling = nAsync;
|
|
|
|
return bRes;
|
|
}
|
|
|
|
SShaderGenComb* CShaderMan::mfGetShaderGenInfo(const char* nmFX)
|
|
{
|
|
SShaderGenComb* c = NULL;
|
|
uint32 i;
|
|
for (i = 0; i < m_SGC.size(); i++)
|
|
{
|
|
c = &m_SGC[i];
|
|
if (!azstricmp(c->Name.c_str(), nmFX))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
SShaderGenComb cmb;
|
|
if (i == m_SGC.size())
|
|
{
|
|
c = NULL;
|
|
cmb.pGen = mfCreateShaderGenInfo(nmFX, false);
|
|
cmb.Name = CCryNameR(nmFX);
|
|
m_SGC.push_back(cmb);
|
|
c = &m_SGC[i];
|
|
}
|
|
return c;
|
|
}
|
|
|
|
static uint64 sGetGL(char** s, CCryNameR& name, uint32& nHWFlags)
|
|
{
|
|
uint32 i;
|
|
|
|
nHWFlags = 0;
|
|
SShaderGenComb* c = NULL;
|
|
const char* m = strchr(name.c_str(), '@');
|
|
if (!m)
|
|
{
|
|
m = strchr(name.c_str(), '/');
|
|
}
|
|
assert(m);
|
|
if (!m)
|
|
{
|
|
return -1;
|
|
}
|
|
char nmFX[128];
|
|
char nameExt[128];
|
|
nameExt[0] = 0;
|
|
cry_strcpy(nmFX, name.c_str(), (size_t)(m - name.c_str()));
|
|
c = gRenDev->m_cEF.mfGetShaderGenInfo(nmFX);
|
|
if (!c || !c->pGen || !c->pGen->m_BitMask.Num())
|
|
{
|
|
return 0;
|
|
}
|
|
uint64 nGL = 0;
|
|
SShaderGen* pG = c->pGen;
|
|
for (i = 0; i < pG->m_BitMask.Num(); i++)
|
|
{
|
|
SShaderGenBit* pBit = pG->m_BitMask[i];
|
|
if (pBit->m_nDependencySet & (SHGD_HW_BILINEARFP16 | SHGD_HW_SEPARATEFP16))
|
|
{
|
|
nHWFlags |= pBit->m_nDependencySet;
|
|
}
|
|
}
|
|
while (true)
|
|
{
|
|
char theChar;
|
|
int n = 0;
|
|
while ((theChar = **s) != 0)
|
|
{
|
|
if (theChar == ')' || theChar == '|')
|
|
{
|
|
nameExt[n] = 0;
|
|
break;
|
|
}
|
|
nameExt[n++] = theChar;
|
|
++*s;
|
|
}
|
|
if (!nameExt[0])
|
|
{
|
|
break;
|
|
}
|
|
for (i = 0; i < pG->m_BitMask.Num(); i++)
|
|
{
|
|
SShaderGenBit* pBit = pG->m_BitMask[i];
|
|
if (!azstricmp(pBit->m_ParamName.c_str(), nameExt))
|
|
{
|
|
nGL |= pBit->m_Mask;
|
|
break;
|
|
}
|
|
}
|
|
if (i == pG->m_BitMask.Num())
|
|
{
|
|
if (!strncmp(nameExt, "0x", 2))
|
|
{
|
|
//nGL |= shGetHex(&nameExt[2]);
|
|
}
|
|
else
|
|
{
|
|
//assert(0);
|
|
if IsCVarConstAccess(constexpr) (CRenderer::CV_r_shadersdebug)
|
|
{
|
|
iLog->Log("WARNING: Couldn't find global flag '%s' in shader '%s' (skipped)", nameExt, c->Name.c_str());
|
|
}
|
|
}
|
|
}
|
|
if (**s == '|')
|
|
{
|
|
++*s;
|
|
}
|
|
}
|
|
return nGL;
|
|
}
|
|
|
|
static uint64 sGetFlag(char** s, SShaderGen* shaderGenInfo)
|
|
{
|
|
if (!shaderGenInfo)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
uint32 i = 0;
|
|
uint64 mask = 0;
|
|
char name[128] = {0};
|
|
while (true)
|
|
{
|
|
char theChar;
|
|
int n = 0;
|
|
while ((theChar = **s) != 0)
|
|
{
|
|
if (theChar == ')' || theChar == '|')
|
|
{
|
|
name[n] = 0;
|
|
break;
|
|
}
|
|
name[n++] = theChar;
|
|
++*s;
|
|
}
|
|
if (!name[0])
|
|
{
|
|
break;
|
|
}
|
|
for (i = 0; i < shaderGenInfo->m_BitMask.Num(); i++)
|
|
{
|
|
SShaderGenBit* pBit = shaderGenInfo->m_BitMask[i];
|
|
if (!azstricmp(pBit->m_ParamName.c_str(), name))
|
|
{
|
|
mask |= pBit->m_Mask;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == shaderGenInfo->m_BitMask.Num())
|
|
{
|
|
AZ_Warning("ShaderCache", false, "Couldn't find runtime flag '%s' (skipped)", name);
|
|
}
|
|
|
|
if (**s == '|')
|
|
{
|
|
++*s;
|
|
}
|
|
}
|
|
return mask;
|
|
}
|
|
|
|
static int sEOF(bool bFromFile, char* pPtr, AZ::IO::HandleType fileHandle)
|
|
{
|
|
int nStatus;
|
|
if (bFromFile)
|
|
{
|
|
nStatus = gEnv->pCryPak->FEof(fileHandle);
|
|
}
|
|
else
|
|
{
|
|
SkipCharacters(&pPtr, kWhiteSpace);
|
|
if (!*pPtr)
|
|
{
|
|
nStatus = 1;
|
|
}
|
|
else
|
|
{
|
|
nStatus = 0;
|
|
}
|
|
}
|
|
return nStatus;
|
|
}
|
|
|
|
void CShaderMan::mfCloseShadersCache(int nID)
|
|
{
|
|
if (m_FPCacheCombinations[nID])
|
|
{
|
|
gEnv->pCryPak->FClose(m_FPCacheCombinations[nID]);
|
|
m_FPCacheCombinations[nID] = 0;
|
|
}
|
|
}
|
|
|
|
void sSkipLine(char*& s)
|
|
{
|
|
if (!s)
|
|
{
|
|
return;
|
|
}
|
|
|
|
char* sEnd = strchr(s, '\n');
|
|
if (sEnd)
|
|
{
|
|
sEnd++;
|
|
s = sEnd;
|
|
}
|
|
}
|
|
|
|
static void sIterateHW_r(FXShaderCacheCombinations* Combinations, SCacheCombination& cmb, int i, uint64 nHW, const char* szName)
|
|
{
|
|
string str;
|
|
gRenDev->m_cEF.mfInsertNewCombination(cmb.Ident, cmb.eCL, szName, 0, &str, false);
|
|
CCryNameR nm = CCryNameR(str.c_str());
|
|
FXShaderCacheCombinationsItor it = Combinations->find(nm);
|
|
if (it == Combinations->end())
|
|
{
|
|
cmb.CacheName = str.c_str();
|
|
Combinations->insert(FXShaderCacheCombinationsItor::value_type(nm, cmb));
|
|
}
|
|
for (int j = i; j < 64; j++)
|
|
{
|
|
if (((uint64)1 << j) & nHW)
|
|
{
|
|
cmb.Ident.m_GLMask &= ~((uint64)1 << j);
|
|
sIterateHW_r(Combinations, cmb, j + 1, nHW, szName);
|
|
cmb.Ident.m_GLMask |= ((uint64)1 << j);
|
|
sIterateHW_r(Combinations, cmb, j + 1, nHW, szName);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CShaderMan::mfGetShaderListPath(stack_string& nameOut, int nType)
|
|
{
|
|
if (nType == 0)
|
|
{
|
|
nameOut = stack_string(m_szCachePath.c_str()) + stack_string("shaders/shaderlist.txt");
|
|
}
|
|
else
|
|
{
|
|
nameOut = stack_string(m_szCachePath.c_str()) + stack_string("shaders/cache/shaderlistactivate.txt");
|
|
}
|
|
}
|
|
|
|
void CShaderMan::mfMergeShadersCombinations(FXShaderCacheCombinations* Combinations, int nType)
|
|
{
|
|
FXShaderCacheCombinationsItor itor;
|
|
for (itor = m_ShaderCacheCombinations[nType].begin(); itor != m_ShaderCacheCombinations[nType].end(); itor++)
|
|
{
|
|
SCacheCombination* cmb = &itor->second;
|
|
FXShaderCacheCombinationsItor it = Combinations->find(cmb->CacheName);
|
|
if (it == Combinations->end())
|
|
{
|
|
Combinations->insert(FXShaderCacheCombinationsItor::value_type(cmb->CacheName, *cmb));
|
|
}
|
|
}
|
|
}
|
|
|
|
//==========================================================================================================================================
|
|
|
|
struct CompareCombItem
|
|
{
|
|
bool operator()(const SCacheCombination& p1, const SCacheCombination& p2) const
|
|
{
|
|
int n = azstricmp(p1.Name.c_str(), p2.Name.c_str());
|
|
if (n)
|
|
{
|
|
return n < 0;
|
|
}
|
|
n = p1.nCount - p2.nCount;
|
|
if (n)
|
|
{
|
|
return n > 0;
|
|
}
|
|
return (azstricmp(p1.CacheName.c_str(), p2.CacheName.c_str()) < 0);
|
|
}
|
|
};
|
|
|
|
#define g_szTestResults "TestResults"
|
|
|
|
void CShaderMan::mfInitShadersCacheMissLog()
|
|
{
|
|
m_ShaderCacheMissCallback = 0;
|
|
m_ShaderCacheMissPath = "";
|
|
|
|
// don't access the HD if we don't have any logging to file enabled
|
|
if (!CRenderer::CV_r_shaderslogcachemisses)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// create valid path
|
|
gEnv->pCryPak->MakeDir(g_szTestResults);
|
|
|
|
m_ShaderCacheMissPath = string("@usercache@\\Shaders\\ShaderCacheMisses.txt"); // do we want this here, or maybe in @log@ ?
|
|
|
|
// load data which is already stored
|
|
AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle;
|
|
gEnv->pFileIO->Open(m_ShaderCacheMissPath.c_str(), AZ::IO::OpenMode::ModeRead, fileHandle);
|
|
if (fileHandle != AZ::IO::InvalidHandle)
|
|
{
|
|
char str[2048];
|
|
int nLine = 0;
|
|
|
|
while (!gEnv->pFileIO->Eof(fileHandle))
|
|
{
|
|
nLine++;
|
|
str[0] = 0;
|
|
AZ::IO::FGetS(str, 2047, fileHandle);
|
|
if (!str[0])
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// remove new line at end
|
|
int len = strlen(str);
|
|
if (len > 0)
|
|
{
|
|
str[strlen(str) - 1] = 0;
|
|
}
|
|
|
|
m_ShaderCacheMisses.push_back(str);
|
|
}
|
|
|
|
std::sort(m_ShaderCacheMisses.begin(), m_ShaderCacheMisses.end());
|
|
|
|
gEnv->pFileIO->Close(fileHandle);
|
|
fileHandle = AZ::IO::InvalidHandle;
|
|
}
|
|
}
|
|
|
|
void CShaderMan::mfInitShadersCache(byte bForLevel, FXShaderCacheCombinations* Combinations, const char* pCombinations, int nType)
|
|
{
|
|
COMPILE_TIME_ASSERT(SHADER_LIST_VER != SHADER_SERIALISE_VER);
|
|
|
|
char str[2048];
|
|
bool bFromFile = (Combinations == NULL);
|
|
stack_string nameComb;
|
|
m_ShaderCacheExportCombinations.clear();
|
|
AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle;
|
|
if (bFromFile)
|
|
{
|
|
if (!gRenDev->IsEditorMode() && !CRenderer::CV_r_shadersdebug && !gRenDev->IsShaderCacheGenMode())
|
|
{
|
|
return;
|
|
}
|
|
mfGetShaderListPath(nameComb, nType);
|
|
fileHandle = gEnv->pCryPak->FOpen(nameComb.c_str(), "r+");
|
|
if (fileHandle == AZ::IO::InvalidHandle)
|
|
{
|
|
fileHandle = gEnv->pCryPak->FOpen(nameComb.c_str(), "w+");
|
|
}
|
|
if (fileHandle == AZ::IO::InvalidHandle)
|
|
{
|
|
AZ::IO::HandleType statusdstFileHandle = AZ::IO::InvalidHandle;
|
|
gEnv->pFileIO->Open(nameComb.c_str(), AZ::IO::OpenMode::ModeRead | AZ::IO::OpenMode::ModeBinary, statusdstFileHandle);
|
|
|
|
if (statusdstFileHandle != AZ::IO::InvalidHandle)
|
|
{
|
|
gEnv->pFileIO->Close(statusdstFileHandle);
|
|
CrySetFileAttributes(str, FILE_ATTRIBUTE_ARCHIVE);
|
|
fileHandle = gEnv->pCryPak->FOpen(nameComb.c_str(), "r+");
|
|
}
|
|
}
|
|
m_FPCacheCombinations[nType] = fileHandle;
|
|
Combinations = &m_ShaderCacheCombinations[nType];
|
|
}
|
|
|
|
int nLine = 0;
|
|
char* pPtr = (char*)pCombinations;
|
|
char* ss;
|
|
if (fileHandle != AZ::IO::InvalidHandle || !bFromFile)
|
|
{
|
|
while (!sEOF(bFromFile, pPtr, fileHandle))
|
|
{
|
|
nLine++;
|
|
|
|
str[0] = 0;
|
|
if (bFromFile)
|
|
{
|
|
gEnv->pCryPak->FGets(str, 2047, fileHandle);
|
|
}
|
|
else
|
|
{
|
|
fxFillCR(&pPtr, str);
|
|
}
|
|
if (!str[0])
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (str[0] == '/' && str[1] == '/') // commented line e.g. // BadLine: Metal@Common_ShadowPS(%BIllum@IlluminationPS(%DIFFUSE|%ENVCMAMB|%ALPHAGLOW|%STAT_BRANCHING)(%_RT_AMBIENT|%_RT_BUMP|%_RT_GLOW)(101)(0)(0)(ps_2_0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
bool bExportEntry = false;
|
|
int size = strlen(str);
|
|
if (str[size - 1] == 0xa)
|
|
{
|
|
str[size - 1] = 0;
|
|
}
|
|
SCacheCombination cmb;
|
|
char* s = str;
|
|
SkipCharacters(&s, kWhiteSpace);
|
|
if (s[0] != '<')
|
|
{
|
|
continue;
|
|
}
|
|
char* st;
|
|
if (!bForLevel)
|
|
{
|
|
int nVer = atoi(&s[1]);
|
|
if (nVer != SHADER_LIST_VER)
|
|
{
|
|
if (nVer == SHADER_SERIALISE_VER && bFromFile)
|
|
{
|
|
bExportEntry = true;
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
if (s[2] != '>')
|
|
{
|
|
continue;
|
|
}
|
|
s += 3;
|
|
}
|
|
else
|
|
{
|
|
st = s;
|
|
s = strchr(&st[1], '>');
|
|
if (!s)
|
|
{
|
|
continue;
|
|
}
|
|
cmb.nCount = atoi(st);
|
|
s++;
|
|
}
|
|
if (bForLevel == 2)
|
|
{
|
|
st = s;
|
|
if (s[0] != '<')
|
|
{
|
|
continue;
|
|
}
|
|
s = strchr(st, '>');
|
|
if (!s)
|
|
{
|
|
continue;
|
|
}
|
|
int nVer = atoi(&st[1]);
|
|
|
|
if (nVer != SHADER_LIST_VER)
|
|
{
|
|
if (nVer == SHADER_SERIALISE_VER)
|
|
{
|
|
bExportEntry = true;
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
s++;
|
|
}
|
|
st = s;
|
|
s = strchr(s, '(');
|
|
char name[128];
|
|
if (s)
|
|
{
|
|
memcpy(name, st, s - st);
|
|
name[s - st] = 0;
|
|
cmb.Name = name;
|
|
s++;
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
uint32 nHW = 0;
|
|
cmb.Ident.m_GLMask = sGetGL(&s, cmb.Name, nHW);
|
|
if (cmb.Ident.m_GLMask == -1)
|
|
{
|
|
const char* szFileName = nameComb.c_str();
|
|
if (szFileName)
|
|
{
|
|
iLog->Log("Error: Error in '%s' file (Line: %d)", szFileName, nLine);
|
|
}
|
|
else
|
|
{
|
|
assert(!bFromFile);
|
|
iLog->Log("Error: Error in non-file shader (Line: %d)", nLine);
|
|
}
|
|
sSkipLine(s);
|
|
iLog->Log("Error: Error in '%s' file (Line: %d)", nameComb.c_str(), nLine);
|
|
return;
|
|
}
|
|
|
|
ss = strchr(s, '(');
|
|
if (!ss)
|
|
{
|
|
sSkipLine(s);
|
|
iLog->Log("Error: Error in '%s' file (Line: %d)", nameComb.c_str(), nLine);
|
|
return;
|
|
}
|
|
s = ++ss;
|
|
cmb.Ident.m_RTMask = sGetFlag(&s, gRenDev->m_cEF.m_pGlobalExt);
|
|
|
|
ss = strchr(s, '(');
|
|
if (!ss)
|
|
{
|
|
sSkipLine(s);
|
|
iLog->Log("Error: Error in '%s' file (Line: %d)", nameComb.c_str(), nLine);
|
|
return;
|
|
}
|
|
s = ++ss;
|
|
cmb.Ident.m_LightMask = shGetHex(s);
|
|
|
|
ss = strchr(s, '(');
|
|
if (!ss)
|
|
{
|
|
sSkipLine(s);
|
|
iLog->Log("Error: Error in '%s' file (Line: %d)", nameComb.c_str(), nLine);
|
|
return;
|
|
}
|
|
s = ++ss;
|
|
cmb.Ident.m_MDMask = shGetHex(s);
|
|
|
|
ss = strchr(s, '(');
|
|
if (!ss)
|
|
{
|
|
sSkipLine(s);
|
|
iLog->Log("Error: Error in '%s' file (Line: %d)", nameComb.c_str(), nLine);
|
|
return;
|
|
}
|
|
s = ++ss;
|
|
cmb.Ident.m_MDVMask = shGetHex(s);
|
|
|
|
ss = strchr(s, '(');
|
|
if (!ss)
|
|
{
|
|
sSkipLine(s);
|
|
iLog->Log("Error: Error in '%s' file (Line: %d)", nameComb.c_str(), nLine);
|
|
return;
|
|
}
|
|
s = ss + 1;
|
|
cmb.Ident.m_pipelineState.opaque = shGetHex64(s);
|
|
|
|
ss = strchr(s, '(');
|
|
if (!ss)
|
|
{
|
|
sSkipLine(s);
|
|
iLog->Log("Error: Error in '%s' file (Line: %d)", nameComb.c_str(), nLine);
|
|
return;
|
|
}
|
|
s = ss + 1;
|
|
cmb.Ident.m_STMask = sGetFlag(&s, gRenDev->m_cEF.m_staticExt);
|
|
|
|
s = strchr(s, '(');
|
|
if (s)
|
|
{
|
|
s++;
|
|
cmb.eCL = CHWShader::mfStringClass(s);
|
|
assert (cmb.eCL < eHWSC_Num);
|
|
}
|
|
else
|
|
{
|
|
cmb.eCL = eHWSC_Num;
|
|
}
|
|
if constexpr (true || cmb.eCL < eHWSC_Num)
|
|
{
|
|
CCryNameR nm = CCryNameR(st);
|
|
if (bExportEntry)
|
|
{
|
|
FXShaderCacheCombinationsItor it = m_ShaderCacheExportCombinations.find(nm);
|
|
if (it == m_ShaderCacheExportCombinations.end())
|
|
{
|
|
cmb.CacheName = nm;
|
|
m_ShaderCacheExportCombinations.insert(FXShaderCacheCombinationsItor::value_type(nm, cmb));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FXShaderCacheCombinationsItor it = Combinations->find(nm);
|
|
if (it != Combinations->end())
|
|
{
|
|
//assert(false);
|
|
}
|
|
else
|
|
{
|
|
cmb.CacheName = nm;
|
|
Combinations->insert(FXShaderCacheCombinationsItor::value_type(nm, cmb));
|
|
}
|
|
if (nHW)
|
|
{
|
|
for (int j = 0; j < 64; j++)
|
|
{
|
|
if (((uint64)1 << j) & nHW)
|
|
{
|
|
cmb.Ident.m_GLMask &= ~((uint64)1 << j);
|
|
sIterateHW_r(Combinations, cmb, j + 1, nHW, name);
|
|
cmb.Ident.m_GLMask |= ((uint64)1 << j);
|
|
sIterateHW_r(Combinations, cmb, j + 1, nHW, name);
|
|
cmb.Ident.m_GLMask &= ~((uint64)1 << j);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
iLog->Log("Error: Error in '%s' file (Line: %d)", nameComb.c_str(), nLine);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#if !defined(CONSOLE) && !defined(NULL_RENDERER)
|
|
static void sResetDepend_r(SShaderGen* pGen, SShaderGenBit* pBit, SCacheCombination& cm)
|
|
{
|
|
if (!pBit->m_DependResets.size())
|
|
{
|
|
return;
|
|
}
|
|
uint32 i, j;
|
|
|
|
for (i = 0; i < pBit->m_DependResets.size(); i++)
|
|
{
|
|
const char* c = pBit->m_DependResets[i].c_str();
|
|
for (j = 0; j < pGen->m_BitMask.Num(); j++)
|
|
{
|
|
SShaderGenBit* pBit1 = pGen->m_BitMask[j];
|
|
if (!azstricmp(pBit1->m_ParamName.c_str(), c))
|
|
{
|
|
cm.Ident.m_RTMask &= ~pBit1->m_Mask;
|
|
sResetDepend_r(pGen, pBit1, cm);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void sSetDepend_r(SShaderGen* pGen, SShaderGenBit* pBit, SCacheCombination& cm)
|
|
{
|
|
if (!pBit->m_DependSets.size())
|
|
{
|
|
return;
|
|
}
|
|
uint32 i, j;
|
|
|
|
for (i = 0; i < pBit->m_DependSets.size(); i++)
|
|
{
|
|
const char* c = pBit->m_DependSets[i].c_str();
|
|
for (j = 0; j < pGen->m_BitMask.Num(); j++)
|
|
{
|
|
SShaderGenBit* pBit1 = pGen->m_BitMask[j];
|
|
if (!azstricmp(pBit1->m_ParamName.c_str(), c))
|
|
{
|
|
cm.Ident.m_RTMask |= pBit1->m_Mask;
|
|
sSetDepend_r(pGen, pBit1, cm);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Support for single light only
|
|
static bool sIterateDL(DWORD& dwDL)
|
|
{
|
|
int nLights = dwDL & 0xf;
|
|
int nType[4];
|
|
int i;
|
|
|
|
if (!nLights)
|
|
{
|
|
dwDL = 1;
|
|
return true;
|
|
}
|
|
for (i = 0; i < nLights; i++)
|
|
{
|
|
nType[i] = (dwDL >> (SLMF_LTYPE_SHIFT + (i * SLMF_LTYPE_BITS))) & ((1 << SLMF_LTYPE_BITS) - 1);
|
|
}
|
|
switch (nLights)
|
|
{
|
|
case 1:
|
|
if ((nType[0] & 3) < 2)
|
|
{
|
|
nType[0]++;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
nLights = 2;
|
|
nType[0] = SLMF_DIRECT;
|
|
nType[1] = SLMF_POINT;
|
|
}
|
|
break;
|
|
case 2:
|
|
if ((nType[0] & 3) == SLMF_DIRECT)
|
|
{
|
|
nType[0] = SLMF_POINT;
|
|
nType[1] = SLMF_POINT;
|
|
}
|
|
else
|
|
{
|
|
nLights = 3;
|
|
nType[0] = SLMF_DIRECT;
|
|
nType[1] = SLMF_POINT;
|
|
nType[2] = SLMF_POINT;
|
|
}
|
|
break;
|
|
case 3:
|
|
if ((nType[0] & 3) == SLMF_DIRECT)
|
|
{
|
|
nType[0] = SLMF_POINT;
|
|
nType[1] = SLMF_POINT;
|
|
nType[2] = SLMF_POINT;
|
|
}
|
|
else
|
|
{
|
|
nLights = 4;
|
|
nType[0] = SLMF_DIRECT;
|
|
nType[1] = SLMF_POINT;
|
|
nType[2] = SLMF_POINT;
|
|
nType[3] = SLMF_POINT;
|
|
}
|
|
break;
|
|
case 4:
|
|
if ((nType[0] & 3) == SLMF_DIRECT)
|
|
{
|
|
nType[0] = SLMF_POINT;
|
|
nType[1] = SLMF_POINT;
|
|
nType[2] = SLMF_POINT;
|
|
nType[3] = SLMF_POINT;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
dwDL = nLights;
|
|
for (i = 0; i < nLights; i++)
|
|
{
|
|
dwDL |= nType[i] << (SLMF_LTYPE_SHIFT + i * SLMF_LTYPE_BITS);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*static bool sIterateDL(DWORD& dwDL)
|
|
{
|
|
int nLights = dwDL & 0xf;
|
|
int nType[4];
|
|
int i;
|
|
|
|
if (!nLights)
|
|
{
|
|
dwDL = 1;
|
|
return true;
|
|
}
|
|
for (i=0; i<nLights; i++)
|
|
{
|
|
nType[i] = (dwDL >> (SLMF_LTYPE_SHIFT + (i*SLMF_LTYPE_BITS))) & ((1<<SLMF_LTYPE_BITS)-1);
|
|
}
|
|
switch (nLights)
|
|
{
|
|
case 1:
|
|
if (!(nType[0] & SLMF_RAE_ENABLED))
|
|
nType[0] |= SLMF_RAE_ENABLED;
|
|
else
|
|
if (nType[0]<2)
|
|
nType[0]++;
|
|
else
|
|
{
|
|
nLights = 2;
|
|
nType[0] = SLMF_DIRECT;
|
|
nType[1] = SLMF_POINT;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (!(nType[0] & SLMF_RAE_ENABLED))
|
|
nType[0] |= SLMF_RAE_ENABLED;
|
|
else
|
|
if (!(nType[1] & SLMF_RAE_ENABLED))
|
|
nType[1] |= SLMF_RAE_ENABLED;
|
|
else
|
|
if (nType[0] == SLMF_DIRECT)
|
|
nType[0] = SLMF_POINT;
|
|
else
|
|
{
|
|
nLights = 3;
|
|
nType[0] = SLMF_DIRECT;
|
|
nType[1] = SLMF_POINT;
|
|
nType[2] = SLMF_POINT;
|
|
}
|
|
break;
|
|
case 3:
|
|
if (!(nType[0] & SLMF_RAE_ENABLED))
|
|
nType[0] |= SLMF_RAE_ENABLED;
|
|
else
|
|
if (!(nType[1] & SLMF_RAE_ENABLED))
|
|
nType[1] |= SLMF_RAE_ENABLED;
|
|
else
|
|
if (!(nType[2] & SLMF_RAE_ENABLED))
|
|
nType[2] |= SLMF_RAE_ENABLED;
|
|
else
|
|
if (nType[0] == SLMF_DIRECT)
|
|
nType[0] = SLMF_POINT;
|
|
else
|
|
{
|
|
nLights = 4;
|
|
nType[0] = SLMF_DIRECT;
|
|
nType[1] = SLMF_POINT;
|
|
nType[2] = SLMF_POINT;
|
|
nType[3] = SLMF_POINT;
|
|
}
|
|
break;
|
|
case 4:
|
|
if (!(nType[0] & SLMF_RAE_ENABLED))
|
|
nType[0] |= SLMF_RAE_ENABLED;
|
|
else
|
|
if (!(nType[1] & SLMF_RAE_ENABLED))
|
|
nType[1] |= SLMF_RAE_ENABLED;
|
|
else
|
|
if (!(nType[2] & SLMF_RAE_ENABLED))
|
|
nType[2] |= SLMF_RAE_ENABLED;
|
|
else
|
|
if (!(nType[3] & SLMF_RAE_ENABLED))
|
|
nType[3] |= SLMF_RAE_ENABLED;
|
|
else
|
|
if (nType[0] == SLMF_DIRECT)
|
|
nType[0] = SLMF_POINT;
|
|
else
|
|
return false;
|
|
}
|
|
dwDL = nLights;
|
|
for (i=0; i<nLights; i++)
|
|
{
|
|
dwDL |= nType[i] << (SLMF_LTYPE_SHIFT + i*SLMF_LTYPE_BITS);
|
|
}
|
|
return true;
|
|
}*/
|
|
|
|
void CShaderMan::mfAddLTCombination(SCacheCombination* cmb, FXShaderCacheCombinations& CmbsMapDst, DWORD dwL)
|
|
{
|
|
char str[1024];
|
|
char sLT[64];
|
|
|
|
SCacheCombination cm;
|
|
cm = *cmb;
|
|
cm.Ident.m_LightMask = dwL;
|
|
|
|
const char* c = strchr(cmb->CacheName.c_str(), ')');
|
|
c = strchr(&c[1], ')');
|
|
int len = (int)(c - cmb->CacheName.c_str() + 1);
|
|
assert(len < sizeof(str));
|
|
cry_strcpy(str, cmb->CacheName.c_str(), len);
|
|
cry_strcat(str, "(");
|
|
azsprintf(sLT, "%x", (uint32)dwL);
|
|
cry_strcat(str, sLT);
|
|
c = strchr(&c[2], ')');
|
|
cry_strcat(str, c);
|
|
CCryNameR nm = CCryNameR(str);
|
|
cm.CacheName = nm;
|
|
FXShaderCacheCombinationsItor it = CmbsMapDst.find(nm);
|
|
if (it == CmbsMapDst.end())
|
|
{
|
|
CmbsMapDst.insert(FXShaderCacheCombinationsItor::value_type(nm, cm));
|
|
}
|
|
}
|
|
|
|
void CShaderMan::mfAddLTCombinations(SCacheCombination* cmb, FXShaderCacheCombinations& CmbsMapDst)
|
|
{
|
|
if (!CRenderer::CV_r_shadersprecachealllights)
|
|
{
|
|
return;
|
|
}
|
|
|
|
DWORD dwL = 0; // 0 lights
|
|
bool bRes = false;
|
|
do
|
|
{
|
|
// !HACK: Do not iterate multiple lights for low spec
|
|
if ((cmb->Ident.m_RTMask & (g_HWSR_MaskBit[HWSR_QUALITY] | g_HWSR_MaskBit[HWSR_QUALITY1])) || (dwL & 0xf) <= 1)
|
|
{
|
|
mfAddLTCombination(cmb, CmbsMapDst, dwL);
|
|
}
|
|
bRes = sIterateDL(dwL);
|
|
} while (bRes);
|
|
}
|
|
|
|
|
|
void CShaderMan::mfAddRTCombination_r(int nComb, FXShaderCacheCombinations& CmbsMapDst, SCacheCombination* cmb, CHWShader* pSH, bool bAutoPrecache)
|
|
{
|
|
uint32 i, j, n;
|
|
uint32 dwType = pSH->m_dwShaderType;
|
|
if (!dwType)
|
|
{
|
|
return;
|
|
}
|
|
for (i = nComb; i < m_pGlobalExt->m_BitMask.Num(); i++)
|
|
{
|
|
SShaderGenBit* pBit = m_pGlobalExt->m_BitMask[i];
|
|
if (bAutoPrecache && !(pBit->m_Flags & (SHGF_AUTO_PRECACHE | SHGF_LOWSPEC_AUTO_PRECACHE)))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Precache this flag on low-spec only
|
|
if (pBit->m_Flags & SHGF_LOWSPEC_AUTO_PRECACHE)
|
|
{
|
|
if (cmb->Ident.m_RTMask & (g_HWSR_MaskBit[HWSR_QUALITY] | g_HWSR_MaskBit[HWSR_QUALITY1]))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
// Only in runtime used
|
|
if (pBit->m_Flags & SHGF_RUNTIME)
|
|
{
|
|
continue;
|
|
}
|
|
for (n = 0; n < pBit->m_PrecacheNames.size(); n++)
|
|
{
|
|
if (pBit->m_PrecacheNames[n] == dwType)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (n < pBit->m_PrecacheNames.size())
|
|
{
|
|
SCacheCombination cm;
|
|
cm = *cmb;
|
|
cm.Ident.m_RTMask &= ~pBit->m_Mask;
|
|
cm.Ident.m_RTMask |= (pBit->m_Mask ^ cmb->Ident.m_RTMask) & pBit->m_Mask;
|
|
if (!bAutoPrecache)
|
|
{
|
|
uint64 nBitSet = pBit->m_Mask & cmb->Ident.m_RTMask;
|
|
if (nBitSet)
|
|
{
|
|
sSetDepend_r(m_pGlobalExt, pBit, cm);
|
|
}
|
|
else
|
|
{
|
|
sResetDepend_r(m_pGlobalExt, pBit, cm);
|
|
}
|
|
}
|
|
|
|
char str[1024];
|
|
const char* c = strchr(cmb->CacheName.c_str(), '(');
|
|
const size_t len = (size_t)(c - cmb->CacheName.c_str());
|
|
cry_strcpy(str, cmb->CacheName.c_str(), len);
|
|
const char* c1 = strchr(&c[1], '(');
|
|
cry_strcat(str, c, (size_t)(c1 - c));
|
|
cry_strcat(str, "(");
|
|
SShaderGen* pG = m_pGlobalExt;
|
|
stack_string sRT;
|
|
for (j = 0; j < pG->m_BitMask.Num(); j++)
|
|
{
|
|
SShaderGenBit* pBit2 = pG->m_BitMask[j];
|
|
if (pBit2->m_Mask & cm.Ident.m_RTMask)
|
|
{
|
|
if (!sRT.empty())
|
|
{
|
|
sRT += "|";
|
|
}
|
|
sRT += pBit2->m_ParamName.c_str();
|
|
}
|
|
}
|
|
cry_strcat(str, sRT.c_str());
|
|
c1 = strchr(&c1[1], ')');
|
|
cry_strcat(str, c1);
|
|
CCryNameR nm = CCryNameR(str);
|
|
cm.CacheName = nm;
|
|
// HACK: don't allow unsupported quality mode
|
|
uint64 nQMask = g_HWSR_MaskBit[HWSR_QUALITY] | g_HWSR_MaskBit[HWSR_QUALITY1];
|
|
if ((cm.Ident.m_RTMask & nQMask) != nQMask)
|
|
{
|
|
FXShaderCacheCombinationsItor it = CmbsMapDst.find(nm);
|
|
if (it == CmbsMapDst.end())
|
|
{
|
|
CmbsMapDst.insert(FXShaderCacheCombinationsItor::value_type(nm, cm));
|
|
}
|
|
}
|
|
if (pSH->m_Flags & (HWSG_SUPPORTS_MULTILIGHTS | HWSG_SUPPORTS_LIGHTING))
|
|
{
|
|
mfAddLTCombinations(&cm, CmbsMapDst);
|
|
}
|
|
mfAddRTCombination_r(i + 1, CmbsMapDst, &cm, pSH, bAutoPrecache);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CShaderMan::mfAddRTCombinations(FXShaderCacheCombinations& CmbsMapSrc, FXShaderCacheCombinations& CmbsMapDst, CHWShader* pSH, bool bListOnly)
|
|
{
|
|
if (pSH->m_nFrameLoad == gRenDev->GetFrameID())
|
|
{
|
|
return;
|
|
}
|
|
pSH->m_nFrameLoad = gRenDev->GetFrameID();
|
|
uint32 dwType = pSH->m_dwShaderType;
|
|
if (!dwType)
|
|
{
|
|
return;
|
|
}
|
|
const char* str2 = pSH->mfGetEntryName();
|
|
FXShaderCacheCombinationsItor itor;
|
|
for (itor = CmbsMapSrc.begin(); itor != CmbsMapSrc.end(); itor++)
|
|
{
|
|
SCacheCombination* cmb = &itor->second;
|
|
const char* c = strchr(cmb->Name.c_str(), '@');
|
|
if (!c)
|
|
{
|
|
c = strchr(cmb->Name.c_str(), '/');
|
|
}
|
|
assert(c);
|
|
if (!c)
|
|
{
|
|
continue;
|
|
}
|
|
if (azstricmp(&c[1], str2))
|
|
{
|
|
continue;
|
|
}
|
|
/*if (!azstricmp(str2, "MetalReflVS"))
|
|
{
|
|
if (cmb->nGL == 0x1093)
|
|
{
|
|
int nnn = 0;
|
|
}
|
|
}*/
|
|
if (bListOnly)
|
|
{
|
|
if (pSH->m_Flags & (HWSG_SUPPORTS_MULTILIGHTS | HWSG_SUPPORTS_LIGHTING))
|
|
{
|
|
mfAddLTCombinations(cmb, CmbsMapDst);
|
|
}
|
|
mfAddRTCombination_r(0, CmbsMapDst, cmb, pSH, true);
|
|
}
|
|
else
|
|
{
|
|
mfAddRTCombination_r(0, CmbsMapDst, cmb, pSH, false);
|
|
}
|
|
}
|
|
}
|
|
#endif // CONSOLE
|
|
|
|
void GenerateMaskString(SShaderGen* shaderInfo, uint64 mask, stack_string& maskStr)
|
|
{
|
|
if (!shaderInfo || !mask)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (unsigned i = 0; i < shaderInfo->m_BitMask.Num(); ++i)
|
|
{
|
|
SShaderGenBit* pBit = shaderInfo->m_BitMask[i];
|
|
if (pBit->m_Mask & mask)
|
|
{
|
|
if (!maskStr.empty())
|
|
{
|
|
maskStr += "|";
|
|
}
|
|
maskStr += pBit->m_ParamName.c_str();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CShaderMan::mfInsertNewCombination(SShaderCombIdent& Ident, EHWShaderClass eCL, const char* name, int nID, string* Str, byte bStore)
|
|
{
|
|
char str[2048];
|
|
if (!m_FPCacheCombinations[nID] && bStore)
|
|
{
|
|
return;
|
|
}
|
|
|
|
stack_string sGL;
|
|
stack_string sRT;
|
|
stack_string staticMask;
|
|
uint32 i, j;
|
|
SShaderGenComb* c = NULL;
|
|
if (Ident.m_GLMask)
|
|
{
|
|
const char* m = strchr(name, '@');
|
|
if (!m)
|
|
{
|
|
m = strchr(name, '/');
|
|
}
|
|
assert(m);
|
|
char nmFX[128];
|
|
if (m)
|
|
{
|
|
cry_strcpy(nmFX, name, (size_t)(m - name));
|
|
c = mfGetShaderGenInfo(nmFX);
|
|
if (c && c->pGen && c->pGen->m_BitMask.Num())
|
|
{
|
|
SShaderGen* pG = c->pGen;
|
|
for (i = 0; i < 64; i++)
|
|
{
|
|
if (Ident.m_GLMask & ((uint64)1 << i))
|
|
{
|
|
for (j = 0; j < pG->m_BitMask.Num(); j++)
|
|
{
|
|
SShaderGenBit* pBit = pG->m_BitMask[j];
|
|
if (pBit->m_Mask & (Ident.m_GLMask & ((uint64)1 << i)))
|
|
{
|
|
if (!sGL.empty())
|
|
{
|
|
sGL += "|";
|
|
}
|
|
sGL += pBit->m_ParamName.c_str();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
GenerateMaskString(m_pGlobalExt, Ident.m_RTMask, sRT);
|
|
GenerateMaskString(m_staticExt, Ident.m_STMask, staticMask);
|
|
|
|
uint32 nLT = Ident.m_LightMask;
|
|
if (bStore == 1 && Ident.m_LightMask)
|
|
{
|
|
nLT = 1;
|
|
}
|
|
azsprintf(str, "<%d>%s(%s)(%s)(%x)(%x)(%x)(%llx)(%s)(%s)", SHADER_LIST_VER, name, sGL.c_str(), sRT.c_str(), nLT, Ident.m_MDMask, Ident.m_MDVMask, Ident.m_pipelineState.opaque, staticMask.c_str(), CHWShader::mfClassString(eCL));
|
|
if (!bStore)
|
|
{
|
|
if (Str)
|
|
{
|
|
*Str = str;
|
|
}
|
|
return;
|
|
}
|
|
CCryNameR nm;
|
|
if (str[0] == '<' && str[2] == '>')
|
|
{
|
|
nm = CCryNameR(&str[3]);
|
|
}
|
|
else
|
|
{
|
|
nm = CCryNameR(str);
|
|
}
|
|
FXShaderCacheCombinationsItor it = m_ShaderCacheCombinations[nID].find(nm);
|
|
if (it != m_ShaderCacheCombinations[nID].end())
|
|
{
|
|
return;
|
|
}
|
|
SCacheCombination cmb;
|
|
cmb.Name = name;
|
|
cmb.CacheName = nm;
|
|
cmb.Ident = Ident;
|
|
cmb.eCL = eCL;
|
|
{
|
|
stack_string nameOut;
|
|
mfGetShaderListPath(nameOut, nID);
|
|
|
|
static CryCriticalSection s_cResLock;
|
|
AUTO_LOCK(s_cResLock); // Not thread safe without this
|
|
|
|
if (m_FPCacheCombinations[nID])
|
|
{
|
|
m_ShaderCacheCombinations[nID].insert(FXShaderCacheCombinationsItor::value_type(nm, cmb));
|
|
gEnv->pCryPak->FPrintf(m_FPCacheCombinations[nID], "%s\n", str);
|
|
gEnv->pCryPak->FFlush(m_FPCacheCombinations[nID]);
|
|
}
|
|
}
|
|
if (Str)
|
|
{
|
|
*Str = str;
|
|
}
|
|
}
|
|
|
|
inline bool sCompareComb(const SCacheCombination& a, const SCacheCombination& b)
|
|
{
|
|
int32 dif;
|
|
|
|
char shader1[128], shader2[128];
|
|
char* tech1 = NULL, * tech2 = NULL;
|
|
azstrcpy(shader1, 128, a.Name.c_str());
|
|
azstrcpy(shader2, 128, b.Name.c_str());
|
|
char* c = strchr(shader1, '@');
|
|
if (!c)
|
|
{
|
|
c = strchr(shader1, '/');
|
|
}
|
|
//assert(c);
|
|
if (c)
|
|
{
|
|
*c = 0;
|
|
tech1 = c + 1;
|
|
}
|
|
c = strchr(shader2, '@');
|
|
if (!c)
|
|
{
|
|
c = strchr(shader2, '/');
|
|
}
|
|
//assert(c);
|
|
if (c)
|
|
{
|
|
*c = 0;
|
|
tech2 = c + 1;
|
|
}
|
|
|
|
dif = azstricmp(shader1, shader2);
|
|
if (dif != 0)
|
|
{
|
|
return dif < 0;
|
|
}
|
|
|
|
if (tech1 == NULL && tech2 != NULL)
|
|
{
|
|
return true;
|
|
}
|
|
if (tech1 != NULL && tech2 == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (tech1 != NULL && tech2 != NULL)
|
|
{
|
|
dif = azstricmp(tech1, tech2);
|
|
if (dif != 0)
|
|
{
|
|
return dif < 0;
|
|
}
|
|
}
|
|
|
|
if (a.Ident.m_GLMask != b.Ident.m_GLMask)
|
|
{
|
|
return a.Ident.m_GLMask < b.Ident.m_GLMask;
|
|
}
|
|
|
|
if (a.Ident.m_STMask != b.Ident.m_STMask)
|
|
{
|
|
return a.Ident.m_STMask < b.Ident.m_STMask;
|
|
}
|
|
|
|
if (a.Ident.m_RTMask != b.Ident.m_RTMask)
|
|
{
|
|
return a.Ident.m_RTMask < b.Ident.m_RTMask;
|
|
}
|
|
|
|
if (a.Ident.m_pipelineState.opaque != b.Ident.m_pipelineState.opaque)
|
|
{
|
|
return a.Ident.m_pipelineState.opaque < b.Ident.m_pipelineState.opaque;
|
|
}
|
|
|
|
if (a.Ident.m_FastCompare1 != b.Ident.m_FastCompare1)
|
|
{
|
|
return a.Ident.m_FastCompare1 < b.Ident.m_FastCompare1;
|
|
}
|
|
|
|
if (a.Ident.m_FastCompare2 != b.Ident.m_FastCompare2)
|
|
{
|
|
return a.Ident.m_FastCompare2 < b.Ident.m_FastCompare2;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
#if !defined(CONSOLE) && !defined(NULL_RENDERER)
|
|
|
|
void CShaderMan::AddGLCombinations(CShader* pSH, std::vector<SCacheCombination>& CmbsGL)
|
|
{
|
|
int i, j;
|
|
uint64 nMask = 0;
|
|
if (pSH->m_pGenShader)
|
|
{
|
|
SShaderGen* pG = pSH->m_pGenShader->m_ShaderGenParams;
|
|
for (i = 0; i < pG->m_BitMask.size(); i++)
|
|
{
|
|
SShaderGenBit* pB = pG->m_BitMask[i];
|
|
SCacheCombination cc;
|
|
cc.Name = pB->m_ParamName;
|
|
for (j = 0; j < m_pGlobalExt->m_BitMask.size(); j++)
|
|
{
|
|
SShaderGenBit* pB1 = m_pGlobalExt->m_BitMask[j];
|
|
if (pB1->m_ParamName == pB->m_ParamName)
|
|
{
|
|
nMask |= pB1->m_Mask;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SCacheCombination cc;
|
|
cc.Ident.m_GLMask = 0;
|
|
CmbsGL.push_back(cc);
|
|
}
|
|
}
|
|
|
|
void CShaderMan::AddGLCombination(FXShaderCacheCombinations& CmbsMap, SCacheCombination& cmb)
|
|
{
|
|
char str[1024];
|
|
const char* st = cmb.CacheName.c_str();
|
|
if (st[0] == '<')
|
|
{
|
|
st += 3;
|
|
}
|
|
const char* s = strchr(st, '@');
|
|
char name[128];
|
|
if (!s)
|
|
{
|
|
s = strchr(st, '/');
|
|
}
|
|
if (s)
|
|
{
|
|
memcpy(name, st, s - st);
|
|
name[s - st] = 0;
|
|
}
|
|
else
|
|
{
|
|
azstrcpy(name, 128, st);
|
|
}
|
|
#ifdef __GNUC__
|
|
sprintf(str, "%s(%llx)(%x)(%x)", name, cmb.Ident.m_GLMask, cmb.Ident.m_MDMask, cmb.Ident.m_MDVMask);
|
|
#else
|
|
azsprintf(str, "%s(%I64x)(%x)(%x)", name, cmb.Ident.m_GLMask, cmb.Ident.m_MDMask, cmb.Ident.m_MDVMask);
|
|
#endif
|
|
CCryNameR nm = CCryNameR(str);
|
|
FXShaderCacheCombinationsItor it = CmbsMap.find(nm);
|
|
if (it == CmbsMap.end())
|
|
{
|
|
cmb.CacheName = nm;
|
|
cmb.Name = nm;
|
|
CmbsMap.insert(FXShaderCacheCombinationsItor::value_type(nm, cmb));
|
|
}
|
|
}
|
|
|
|
void CShaderMan::AddCombination(SCacheCombination& cmb, FXShaderCacheCombinations& CmbsMap, [[maybe_unused]] CHWShader* pHWS)
|
|
{
|
|
char str[2048];
|
|
azsprintf(str, "%s(%llx)(%llx)(%d)(%d)(%d)(%llx)(%llx)", cmb.Name.c_str(), cmb.Ident.m_GLMask, cmb.Ident.m_RTMask, cmb.Ident.m_LightMask, cmb.Ident.m_MDMask, cmb.Ident.m_MDVMask, cmb.Ident.m_pipelineState.opaque, cmb.Ident.m_STMask);
|
|
CCryNameR nm = CCryNameR(str);
|
|
FXShaderCacheCombinationsItor it = CmbsMap.find(nm);
|
|
if (it == CmbsMap.end())
|
|
{
|
|
cmb.CacheName = nm;
|
|
CmbsMap.insert(FXShaderCacheCombinationsItor::value_type(nm, cmb));
|
|
}
|
|
}
|
|
|
|
void CShaderMan::AddLTCombinations(SCacheCombination& cmb, FXShaderCacheCombinations& CmbsMap, CHWShader* pHWS)
|
|
{
|
|
assert(pHWS->m_Flags & HWSG_SUPPORTS_LIGHTING);
|
|
|
|
// Just single light support
|
|
|
|
// Directional light
|
|
cmb.Ident.m_LightMask = 1;
|
|
AddCombination(cmb, CmbsMap, pHWS);
|
|
|
|
// Point light
|
|
cmb.Ident.m_LightMask = 0x101;
|
|
AddCombination(cmb, CmbsMap, pHWS);
|
|
|
|
// Projected light
|
|
cmb.Ident.m_LightMask = 0x201;
|
|
AddCombination(cmb, CmbsMap, pHWS);
|
|
}
|
|
|
|
void CShaderMan::AddRTCombinations(FXShaderCacheCombinations& CmbsMap, CHWShader* pHWS, CShader* pSH, FXShaderCacheCombinations* Combinations)
|
|
{
|
|
SCacheCombination cmb;
|
|
|
|
uint32 nType = pHWS->m_dwShaderType;
|
|
|
|
uint32 i, j;
|
|
SShaderGen* pGen = m_pGlobalExt;
|
|
int nBits = 0;
|
|
|
|
uint32 nBitsPlatform = 0;
|
|
if (CParserBin::m_nPlatform == SF_ORBIS)
|
|
{
|
|
nBitsPlatform |= SHGD_HW_ORBIS;
|
|
}
|
|
else
|
|
if (CParserBin::m_nPlatform == SF_D3D11)
|
|
{
|
|
nBitsPlatform |= SHGD_HW_DX11;
|
|
}
|
|
else
|
|
if (CParserBin::m_nPlatform == SF_GL4)
|
|
{
|
|
nBitsPlatform |= SHGD_HW_GL4;
|
|
}
|
|
else
|
|
if (CParserBin::m_nPlatform == SF_GLES3)
|
|
{
|
|
nBitsPlatform |= SHGD_HW_GLES3;
|
|
}
|
|
// Confetti Nicholas Baldwin: adding metal shader language support
|
|
else
|
|
if (CParserBin::m_nPlatform == SF_METAL)
|
|
{
|
|
nBitsPlatform |= SHGD_HW_METAL;
|
|
}
|
|
|
|
uint64 BitMask[64];
|
|
memset(BitMask, 0, sizeof(BitMask));
|
|
|
|
// Make a mask of flags affected by this type of shader
|
|
uint64 nRTMask = 0;
|
|
uint64 nSetMask = 0;
|
|
|
|
if (nType)
|
|
{
|
|
for (i = 0; i < pGen->m_BitMask.size(); i++)
|
|
{
|
|
SShaderGenBit* pBit = pGen->m_BitMask[i];
|
|
if (!pBit->m_Mask)
|
|
{
|
|
continue;
|
|
}
|
|
if (pBit->m_Flags & SHGF_RUNTIME)
|
|
{
|
|
continue;
|
|
}
|
|
if (nBitsPlatform & pBit->m_nDependencyReset)
|
|
{
|
|
continue;
|
|
}
|
|
for (j = 0; j < pBit->m_PrecacheNames.size(); j++)
|
|
{
|
|
if (pBit->m_PrecacheNames[j] == nType)
|
|
{
|
|
if (nBitsPlatform & pBit->m_nDependencySet)
|
|
{
|
|
nSetMask |= pBit->m_Mask;
|
|
continue;
|
|
}
|
|
BitMask[nBits++] = pBit->m_Mask;
|
|
nRTMask |= pBit->m_Mask;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (nBits > 10)
|
|
{
|
|
CryLog("WARNING: Number of runtime bits for shader '%s' - %d: exceed 10 (too many combinations will be produced)...", pHWS->GetName(), nBits);
|
|
}
|
|
if (nBits > 30)
|
|
{
|
|
CryLog("Error: Ignore...");
|
|
return;
|
|
}
|
|
|
|
cmb.eCL = pHWS->m_eSHClass;
|
|
string szName = string(pSH->GetName());
|
|
szName += string("@") + string(pHWS->m_EntryFunc.c_str());
|
|
cmb.Name = szName;
|
|
cmb.Ident.m_GLMask = pHWS->m_nMaskGenShader;
|
|
|
|
// For unknown shader type just add combinations from the list
|
|
if (!nType)
|
|
{
|
|
FXShaderCacheCombinationsItor itor;
|
|
for (itor = Combinations->begin(); itor != Combinations->end(); itor++)
|
|
{
|
|
SCacheCombination* c = &itor->second;
|
|
if (c->Name == cmb.Name && c->Ident.m_GLMask == pHWS->m_nMaskGenFX)
|
|
{
|
|
cmb = *c;
|
|
AddCombination(cmb, CmbsMap, pHWS);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
cmb.Ident.m_LightMask = 0;
|
|
cmb.Ident.m_MDMask = 0;
|
|
cmb.Ident.m_MDVMask = 0;
|
|
cmb.Ident.m_RTMask = 0;
|
|
cmb.Ident.m_STMask = 0;
|
|
|
|
int nIterations = 1 << nBits;
|
|
for (i = 0; i < nIterations; i++)
|
|
{
|
|
cmb.Ident.m_RTMask = nSetMask;
|
|
cmb.Ident.m_LightMask = 0;
|
|
for (j = 0; j < nBits; j++)
|
|
{
|
|
if ((1 << j) & i)
|
|
{
|
|
cmb.Ident.m_RTMask |= BitMask[j];
|
|
}
|
|
}
|
|
/*if (cmb.nRT == 0x40002)
|
|
{
|
|
int nnn = 0;
|
|
}*/
|
|
AddCombination(cmb, CmbsMap, pHWS);
|
|
if (pHWS->m_Flags & HWSG_SUPPORTS_LIGHTING)
|
|
{
|
|
AddLTCombinations(cmb, CmbsMap, pHWS);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CShaderMan::_PrecacheShaderList(bool bStatsOnly)
|
|
{
|
|
float t0 = gEnv->pTimer->GetAsyncCurTime();
|
|
|
|
if (!m_pGlobalExt)
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_eCacheMode = eSC_BuildGlobalList;
|
|
|
|
uint32 nSaveFeatures = gRenDev->m_Features;
|
|
int nAsync = CRenderer::CV_r_shadersasynccompiling;
|
|
if (nAsync != 3)
|
|
{
|
|
CRenderer::CV_r_shadersasynccompiling = 0;
|
|
}
|
|
|
|
// Command line shaders precaching
|
|
gRenDev->m_Features |= RFT_HW_SM20 | RFT_HW_SM2X | RFT_HW_SM30;
|
|
m_bActivatePhase = false;
|
|
FXShaderCacheCombinations* Combinations = &m_ShaderCacheCombinations[0];
|
|
|
|
std::vector<SCacheCombination> Cmbs;
|
|
std::vector<SCacheCombination> CmbsRT;
|
|
FXShaderCacheCombinations CmbsMap;
|
|
char str1[128], str2[128];
|
|
|
|
// Extract global combinations only (including MD and MDV)
|
|
for (FXShaderCacheCombinationsItor itor = Combinations->begin(); itor != Combinations->end(); itor++)
|
|
{
|
|
SCacheCombination* cmb = &itor->second;
|
|
FXShaderCacheCombinationsItor it = CmbsMap.find(cmb->CacheName);
|
|
if (it == CmbsMap.end())
|
|
{
|
|
CmbsMap.insert(FXShaderCacheCombinationsItor::value_type(cmb->CacheName, *cmb));
|
|
}
|
|
}
|
|
for (FXShaderCacheCombinationsItor itor = CmbsMap.begin(); itor != CmbsMap.end(); itor++)
|
|
{
|
|
SCacheCombination* cmb = &itor->second;
|
|
Cmbs.push_back(*cmb);
|
|
}
|
|
|
|
mfExportShaders();
|
|
|
|
int nEmpty = 0;
|
|
int nProcessed = 0;
|
|
int nCompiled = 0;
|
|
int nMaterialCombinations = 0;
|
|
|
|
if (Cmbs.size() >= 1)
|
|
{
|
|
std::stable_sort(Cmbs.begin(), Cmbs.end(), sCompareComb);
|
|
|
|
nMaterialCombinations = Cmbs.size();
|
|
|
|
m_nCombinationsProcess = Cmbs.size();
|
|
m_bReload = true;
|
|
m_nCombinationsCompiled = 0;
|
|
m_nCombinationsEmpty = 0;
|
|
uint64 nGLLast = -1;
|
|
for (int i = 0; i < Cmbs.size(); i++)
|
|
{
|
|
SCacheCombination* cmb = &Cmbs[i];
|
|
azstrcpy(str1, 128, cmb->Name.c_str());
|
|
char* c = strchr(str1, '@');
|
|
if (!c)
|
|
{
|
|
c = strchr(str1, '/');
|
|
}
|
|
//assert(c);
|
|
if (c)
|
|
{
|
|
*c = 0;
|
|
m_szShaderPrecache = &c[1];
|
|
}
|
|
else
|
|
{
|
|
c = strchr(str1, '(');
|
|
if (c)
|
|
{
|
|
*c = 0;
|
|
m_szShaderPrecache = "";
|
|
}
|
|
}
|
|
gRenDev->m_RP.m_FlagsShader_RT = 0;
|
|
gRenDev->m_RP.m_FlagsShader_LT = 0;
|
|
gRenDev->m_RP.m_FlagsShader_MD = 0;
|
|
gRenDev->m_RP.m_FlagsShader_MDV = 0;
|
|
m_staticFlags = cmb->Ident.m_STMask;
|
|
CShader* pSH = CShaderMan::mfForName(str1, 0, NULL, cmb->Ident.m_GLMask);
|
|
|
|
gRenDev->m_RP.m_pShader = pSH;
|
|
assert(gRenDev->m_RP.m_pShader != 0);
|
|
|
|
std::vector<SCacheCombination>* pCmbs = &Cmbs;
|
|
FXShaderCacheCombinations CmbsMapRTSrc;
|
|
FXShaderCacheCombinations CmbsMapRTDst;
|
|
|
|
for (int m = 0; m < pSH->m_HWTechniques.Num(); m++)
|
|
{
|
|
SShaderTechnique* pTech = pSH->m_HWTechniques[m];
|
|
for (int n = 0; n < pTech->m_Passes.Num(); n++)
|
|
{
|
|
SShaderPass* pPass = &pTech->m_Passes[n];
|
|
if (pPass->m_PShader)
|
|
{
|
|
pPass->m_PShader->m_nFrameLoad = -10;
|
|
}
|
|
if (pPass->m_VShader)
|
|
{
|
|
pPass->m_VShader->m_nFrameLoad = -10;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (; i < Cmbs.size(); i++)
|
|
{
|
|
SCacheCombination* cmba = &Cmbs[i];
|
|
azstrcpy(str2, 128, cmba->Name.c_str());
|
|
c = strchr(str2, '@');
|
|
if (!c)
|
|
{
|
|
c = strchr(str2, '/');
|
|
}
|
|
assert(c);
|
|
if (c)
|
|
{
|
|
*c = 0;
|
|
}
|
|
if (azstricmp(str1, str2) || cmb->Ident.m_GLMask != cmba->Ident.m_GLMask || cmb->Ident.m_STMask != cmba->Ident.m_STMask)
|
|
{
|
|
break;
|
|
}
|
|
CmbsMapRTSrc.insert(FXShaderCacheCombinationsItor::value_type(cmba->CacheName, *cmba));
|
|
}
|
|
// surrounding for(;;i++) will increment this again
|
|
i--;
|
|
m_nCombinationsProcess -= CmbsMapRTSrc.size();
|
|
|
|
for (FXShaderCacheCombinationsItor itor = CmbsMapRTSrc.begin(); itor != CmbsMapRTSrc.end(); itor++)
|
|
{
|
|
SCacheCombination* cmb2 = &itor->second;
|
|
azstrcpy(str2, 128, cmb2->Name.c_str());
|
|
FXShaderCacheCombinationsItor it = CmbsMapRTDst.find(cmb2->CacheName);
|
|
if (it == CmbsMapRTDst.end())
|
|
{
|
|
CmbsMapRTDst.insert(FXShaderCacheCombinationsItor::value_type(cmb2->CacheName, *cmb2));
|
|
}
|
|
}
|
|
|
|
for (int m = 0; m < pSH->m_HWTechniques.Num(); m++)
|
|
{
|
|
SShaderTechnique* pTech = pSH->m_HWTechniques[m];
|
|
for (int n = 0; n < pTech->m_Passes.Num(); n++)
|
|
{
|
|
SShaderPass* pPass = &pTech->m_Passes[n];
|
|
if (pPass->m_PShader)
|
|
{
|
|
mfAddRTCombinations(CmbsMapRTSrc, CmbsMapRTDst, pPass->m_PShader, true);
|
|
}
|
|
if (pPass->m_VShader)
|
|
{
|
|
mfAddRTCombinations(CmbsMapRTSrc, CmbsMapRTDst, pPass->m_VShader, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
CmbsRT.clear();
|
|
for (FXShaderCacheCombinationsItor itor = CmbsMapRTDst.begin(); itor != CmbsMapRTDst.end(); itor++)
|
|
{
|
|
SCacheCombination* cmb2 = &itor->second;
|
|
CmbsRT.push_back(*cmb2);
|
|
}
|
|
pCmbs = &CmbsRT;
|
|
m_nCombinationsProcessOverall = CmbsRT.size();
|
|
m_nCombinationsProcess = 0;
|
|
|
|
CmbsMapRTDst.clear();
|
|
CmbsMapRTSrc.clear();
|
|
uint32 nFlags = HWSF_PRECACHE | HWSF_STOREDATA;
|
|
if (bStatsOnly)
|
|
{
|
|
nFlags |= HWSF_FAKE;
|
|
}
|
|
for (int jj = 0; jj < pCmbs->size(); jj++)
|
|
{
|
|
m_nCombinationsProcess++;
|
|
SCacheCombination* cmba = &(*pCmbs)[jj];
|
|
c = (char*)strchr(cmba->Name.c_str(), '@');
|
|
if (!c)
|
|
{
|
|
c = (char*)strchr(cmba->Name.c_str(), '/');
|
|
}
|
|
assert(c);
|
|
if (!c)
|
|
{
|
|
continue;
|
|
}
|
|
m_szShaderPrecache = &c[1];
|
|
for (int m = 0; m < pSH->m_HWTechniques.Num(); m++)
|
|
{
|
|
SShaderTechnique* pTech = pSH->m_HWTechniques[m];
|
|
for (int n = 0; n < pTech->m_Passes.Num(); n++)
|
|
{
|
|
SShaderPass* pPass = &pTech->m_Passes[n];
|
|
gRenDev->m_RP.m_FlagsShader_RT = cmba->Ident.m_RTMask;
|
|
gRenDev->m_RP.m_FlagsShader_LT = cmba->Ident.m_LightMask;
|
|
gRenDev->m_RP.m_FlagsShader_MD = cmba->Ident.m_MDMask;
|
|
gRenDev->m_RP.m_FlagsShader_MDV = cmba->Ident.m_MDVMask;
|
|
// Adjust some flags for low spec
|
|
CHWShader* shaders[] = { pPass->m_PShader, pPass->m_VShader };
|
|
for (int i2 = 0; i2 < 2; i2++)
|
|
{
|
|
CHWShader* shader = shaders[i2];
|
|
if (shader && (!m_szShaderPrecache || !azstricmp(m_szShaderPrecache, shader->m_EntryFunc.c_str()) != 0))
|
|
{
|
|
uint64 nFlagsOrigShader_RT = gRenDev->m_RP.m_FlagsShader_RT & shader->m_nMaskAnd_RT | shader->m_nMaskOr_RT;
|
|
uint64 nFlagsOrigShader_GL = shader->m_nMaskGenShader;
|
|
uint32 nFlagsOrigShader_LT = gRenDev->m_RP.m_FlagsShader_LT;
|
|
|
|
shader->mfSetV(nFlags);
|
|
|
|
if (nFlagsOrigShader_RT != gRenDev->m_RP.m_FlagsShader_RT || nFlagsOrigShader_GL != shader->m_nMaskGenShader || nFlagsOrigShader_LT != gRenDev->m_RP.m_FlagsShader_LT)
|
|
{
|
|
m_nCombinationsEmpty++;
|
|
if (!bStatsOnly)
|
|
{
|
|
shader->mfAddEmptyCombination(pSH, nFlagsOrigShader_RT, nFlagsOrigShader_GL, nFlagsOrigShader_LT);
|
|
}
|
|
shader->m_nMaskGenShader = nFlagsOrigShader_GL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (CParserBin::m_nPlatform == SF_D3D11 || CParserBin::m_nPlatform == SF_JASPER || CParserBin::m_nPlatform == SF_ORBIS || CParserBin::m_nPlatform == SF_GL4)
|
|
{
|
|
CHWShader* d3d11Shaders[] = { pPass->m_GShader, pPass->m_HShader, pPass->m_CShader, pPass->m_DShader };
|
|
for (int i2 = 0; i2 < 4; i2++)
|
|
{
|
|
CHWShader* shader = d3d11Shaders[i2];
|
|
if (shader && (!m_szShaderPrecache || !azstricmp(m_szShaderPrecache, shader->m_EntryFunc.c_str()) != 0))
|
|
{
|
|
shader->mfSetV(nFlags);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bStatsOnly)
|
|
{
|
|
static int nLastCombs = 0;
|
|
if (m_nCombinationsCompiled != nLastCombs && !(m_nCombinationsCompiled & 0x7f))
|
|
{
|
|
nLastCombs = m_nCombinationsCompiled;
|
|
CryLog("-- Processed: %d, Compiled: %d, Referenced (Empty): %d...", m_nCombinationsProcess, m_nCombinationsCompiled, m_nCombinationsEmpty);
|
|
}
|
|
}
|
|
#ifdef WIN32
|
|
if (!m_bActivatePhase)
|
|
{
|
|
AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::PumpSystemEventLoopUntilEmpty);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
pSH->mfFlushPendedShaders();
|
|
iLog->Update();
|
|
IRenderer* pRenderer = gEnv->pRenderer;
|
|
if (pRenderer)
|
|
{
|
|
pRenderer->FlushRTCommands(true, true, true);
|
|
}
|
|
}
|
|
|
|
pSH->mfFlushCache();
|
|
|
|
// HACK HACK HACK:
|
|
// should be bigger than 0, but could cause issues right now when checking for RT
|
|
// combinations when no shadertype is defined and the previous shader line
|
|
// was still async compiling -- needs fix in HWShader for m_nMaskGenFX
|
|
CHWShader::mfFlushPendedShadersWait(0);
|
|
if (!m_bActivatePhase)
|
|
{
|
|
SAFE_RELEASE(pSH);
|
|
}
|
|
|
|
nProcessed += m_nCombinationsProcess;
|
|
nCompiled += m_nCombinationsCompiled;
|
|
nEmpty += m_nCombinationsEmpty;
|
|
|
|
m_nCombinationsProcess = 0;
|
|
m_nCombinationsCompiled = 0;
|
|
m_nCombinationsEmpty = 0;
|
|
}
|
|
}
|
|
CHWShader::mfFlushPendedShadersWait(-1);
|
|
|
|
|
|
// Optimise shader resources
|
|
SOptimiseStats Stats;
|
|
for (FXShaderCacheNamesItor it = CHWShader::m_ShaderCacheList.begin(); it != CHWShader::m_ShaderCacheList.end(); it++)
|
|
{
|
|
const char* szName = it->first.c_str();
|
|
SShaderCache* c = CHWShader::mfInitCache(szName, NULL, false, it->second, false);
|
|
if (c)
|
|
{
|
|
SOptimiseStats _Stats;
|
|
CHWShader::mfOptimiseCacheFile(c, false, &_Stats);
|
|
Stats.nEntries += _Stats.nEntries;
|
|
Stats.nUniqueEntries += _Stats.nUniqueEntries;
|
|
Stats.nSizeCompressed += _Stats.nSizeCompressed;
|
|
Stats.nSizeUncompressed += _Stats.nSizeUncompressed;
|
|
Stats.nTokenDataSize += _Stats.nTokenDataSize;
|
|
}
|
|
c->Release();
|
|
}
|
|
|
|
CHWShader::m_ShaderCacheList.clear();
|
|
|
|
m_eCacheMode = eSC_Normal;
|
|
m_bReload = false;
|
|
m_szShaderPrecache = NULL;
|
|
m_bActivatePhase = false;
|
|
CRenderer::CV_r_shadersasynccompiling = nAsync;
|
|
|
|
gRenDev->m_Features = nSaveFeatures;
|
|
|
|
float t1 = gEnv->pTimer->GetAsyncCurTime();
|
|
CryLogAlways("All shaders combinations compiled in %.2f seconds", (t1 - t0));
|
|
CryLogAlways("Combinations: (Material: %d, Processed: %d; Compiled: %d; Removed: %d)", nMaterialCombinations, nProcessed, nCompiled, nEmpty);
|
|
CryLogAlways("-- Shader cache overall stats: Entries: %d, Unique Entries: %d, Size: %d, Compressed Size: %d, Token data size: %d", Stats.nEntries, Stats.nUniqueEntries, Stats.nSizeUncompressed, Stats.nSizeCompressed, Stats.nTokenDataSize);
|
|
|
|
m_nCombinationsProcess = -1;
|
|
m_nCombinationsCompiled = -1;
|
|
m_nCombinationsEmpty = -1;
|
|
}
|
|
|
|
#endif
|
|
|
|
void CHWShader::mfGenName(uint64 GLMask, uint64 RTMask, uint32 LightMask, uint32 MDMask, uint32 MDVMask, uint64 PSS, uint64 STMask, EHWShaderClass eClass, char* dstname, int nSize, byte bType)
|
|
{
|
|
assert(dstname);
|
|
dstname[0] = 0;
|
|
|
|
char str[32];
|
|
if (bType != 0 && GLMask)
|
|
{
|
|
azsprintf(str, "(GL%llx)", GLMask);
|
|
cry_strcat(dstname, nSize, str);
|
|
}
|
|
if (bType != 0)
|
|
{
|
|
azsprintf(str, "(RT%llx)", RTMask);
|
|
cry_strcat(dstname, nSize, str);
|
|
}
|
|
if (bType != 0 && LightMask)
|
|
{
|
|
azsprintf(str, "(LT%x)", LightMask);
|
|
cry_strcat(dstname, nSize, str);
|
|
}
|
|
if (bType != 0 && MDMask)
|
|
{
|
|
azsprintf(str, "(MD%x)", MDMask);
|
|
cry_strcat(dstname, nSize, str);
|
|
}
|
|
if (bType != 0 && MDVMask)
|
|
{
|
|
azsprintf(str, "(MDV%x)", MDVMask);
|
|
cry_strcat(dstname, nSize, str);
|
|
}
|
|
if (bType != 0 && PSS)
|
|
{
|
|
azsprintf(str, "(PSS%llx)", PSS);
|
|
cry_strcat(dstname, nSize, str);
|
|
}
|
|
if (bType != 0 && STMask)
|
|
{
|
|
azsprintf(str, "(ST%llx)", STMask);
|
|
cry_strcat(dstname, nSize, str);
|
|
}
|
|
if (bType != 0)
|
|
{
|
|
azsprintf(str, "(%s)", mfClassString(eClass));
|
|
cry_strcat(dstname, nSize, str);
|
|
}
|
|
}
|
|
|
|
#if !defined(CONSOLE) && !defined(NULL_RENDERER)
|
|
|
|
void CShaderMan::mfPrecacheShaders(bool bStatsOnly)
|
|
{
|
|
AZ_Assert(CRenderer::CV_r_shadersPlatform != static_cast<int>(AZ::PlatformID::PLATFORM_MAX), "You must set a shaders platform (r_shadersPlatform) before precaching the shaders");
|
|
CHWShader::mfFlushPendedShadersWait(-1);
|
|
|
|
if (CRenderer::CV_r_shadersorbis)
|
|
{
|
|
#ifdef WATER_TESSELLATION_RENDERER
|
|
CRenderer::CV_r_WaterTessellationHW = 0;
|
|
#endif
|
|
gRenDev->m_bDeviceSupportsFP16Filter = true;
|
|
gRenDev->m_bDeviceSupportsFP16Separate = false;
|
|
gRenDev->m_bDeviceSupportsGeometryShaders = true;
|
|
gRenDev->m_Features |= RFT_HW_SM30;
|
|
|
|
CParserBin::m_bShaderCacheGen = true;
|
|
|
|
gRenDev->m_Features |= RFT_HW_SM50;
|
|
CParserBin::SetupForOrbis();
|
|
CryLogAlways("\nStarting shader compilation for Orbis...");
|
|
mfInitShadersList(NULL);
|
|
mfPreloadShaderExts();
|
|
_PrecacheShaderList(bStatsOnly);
|
|
}
|
|
else
|
|
if (CRenderer::CV_r_shadersdx11)
|
|
{
|
|
gRenDev->m_bDeviceSupportsFP16Filter = true;
|
|
gRenDev->m_bDeviceSupportsFP16Separate = false;
|
|
gRenDev->m_bDeviceSupportsTessellation = true;
|
|
gRenDev->m_bDeviceSupportsGeometryShaders = true;
|
|
gRenDev->m_Features |= RFT_HW_SM30;
|
|
|
|
CParserBin::m_bShaderCacheGen = true;
|
|
|
|
gRenDev->m_Features |= RFT_HW_SM50;
|
|
CParserBin::SetupForD3D11();
|
|
CryLogAlways("\nStarting shader compilation for D3D11...");
|
|
mfInitShadersList(NULL);
|
|
mfPreloadShaderExts();
|
|
_PrecacheShaderList(bStatsOnly);
|
|
}
|
|
else
|
|
if (CRenderer::CV_r_shadersGL4)
|
|
{
|
|
gRenDev->m_bDeviceSupportsFP16Filter = true;
|
|
gRenDev->m_bDeviceSupportsFP16Separate = false;
|
|
gRenDev->m_bDeviceSupportsTessellation = true;
|
|
gRenDev->m_bDeviceSupportsGeometryShaders = true;
|
|
gRenDev->m_Features |= RFT_HW_SM30;
|
|
|
|
CParserBin::m_bShaderCacheGen = true;
|
|
|
|
gRenDev->m_Features |= RFT_HW_SM50;
|
|
CParserBin::SetupForGL4();
|
|
CryLogAlways("\nStarting shader compilation for GLSL 4...");
|
|
mfInitShadersList(NULL);
|
|
mfPreloadShaderExts();
|
|
_PrecacheShaderList(bStatsOnly);
|
|
}
|
|
else
|
|
if (CRenderer::CV_r_shadersGLES3)
|
|
{
|
|
gRenDev->m_bDeviceSupportsFP16Filter = true;
|
|
gRenDev->m_bDeviceSupportsFP16Separate = false;
|
|
gRenDev->m_bDeviceSupportsTessellation = false;
|
|
gRenDev->m_bDeviceSupportsGeometryShaders = false;
|
|
gRenDev->m_Features |= RFT_HW_SM30;
|
|
|
|
CParserBin::m_bShaderCacheGen = true;
|
|
|
|
gRenDev->m_Features |= RFT_HW_SM50;
|
|
CParserBin::SetupForGLES3();
|
|
CryLogAlways("\nStarting shader compilation for GLSL-ES 3...");
|
|
mfInitShadersList(NULL);
|
|
mfPreloadShaderExts();
|
|
_PrecacheShaderList(bStatsOnly);
|
|
}
|
|
else
|
|
if (CRenderer::CV_r_shadersMETAL)
|
|
{
|
|
AZ_Assert(CRenderer::CV_r_shadersPlatform == static_cast<int>(AZ::PlatformID::PLATFORM_APPLE_OSX) || CRenderer::CV_r_shadersPlatform == static_cast<int>(AZ::PlatformID::PLATFORM_APPLE_IOS), "Invalid platform (%d) for metal shaders", CRenderer::CV_r_shadersPlatform);
|
|
gRenDev->m_bDeviceSupportsFP16Filter = true;
|
|
gRenDev->m_bDeviceSupportsFP16Separate = false;
|
|
gRenDev->m_bDeviceSupportsTessellation = false;
|
|
gRenDev->m_bDeviceSupportsGeometryShaders = false;
|
|
gRenDev->m_Features |= RFT_HW_SM30;
|
|
|
|
CParserBin::m_bShaderCacheGen = true;
|
|
|
|
gRenDev->m_Features |= RFT_HW_SM50;
|
|
CParserBin::SetupForMETAL();
|
|
CryLogAlways("\nStarting shader compilation for METAL...");
|
|
mfInitShadersList(NULL);
|
|
mfPreloadShaderExts();
|
|
_PrecacheShaderList(bStatsOnly);
|
|
}
|
|
|
|
#if defined(AZ_PLATFORM_WINDOWS)
|
|
CRenderer::CV_r_shadersPlatform = static_cast<int>(AZ::PlatformID::PLATFORM_WINDOWS_64);
|
|
CParserBin::SetupForD3D11();
|
|
#elif defined(AZ_PLATFORM_MAC)
|
|
CRenderer::CV_r_shadersPlatform = static_cast<int>(AZ::PlatformID::PLATFORM_APPLE_OSX);
|
|
CParserBin::SetupForMETAL();
|
|
#endif
|
|
|
|
gRenDev->m_cEF.m_Bin.InvalidateCache();
|
|
}
|
|
|
|
void CShaderMan::mfGetShaderList()
|
|
{
|
|
if (CRenderer::CV_r_shadersorbis)
|
|
{
|
|
CParserBin::m_bShaderCacheGen = true;
|
|
CParserBin::SetupForOrbis();
|
|
}
|
|
else
|
|
if (CRenderer::CV_r_shadersdx11)
|
|
{
|
|
CParserBin::m_bShaderCacheGen = true;
|
|
CParserBin::SetupForD3D11();
|
|
CryLogAlways("\nGet shader list for D3D11...");
|
|
}
|
|
else
|
|
if (CRenderer::CV_r_shadersGL4)
|
|
{
|
|
CParserBin::m_bShaderCacheGen = true;
|
|
CParserBin::SetupForGL4();
|
|
CryLogAlways("\nGet shader list for GLSL 4...");
|
|
}
|
|
else
|
|
if (CRenderer::CV_r_shadersGLES3)
|
|
{
|
|
CParserBin::m_bShaderCacheGen = true;
|
|
CParserBin::SetupForGLES3();
|
|
CryLogAlways("\nGet shader list for GLSL-ES 3...");
|
|
}
|
|
else
|
|
if (CRenderer::CV_r_shadersMETAL)
|
|
{
|
|
CParserBin::m_bShaderCacheGen = true;
|
|
CParserBin::SetupForMETAL();
|
|
CryLogAlways("\nGet shader list for METAL...");
|
|
}
|
|
|
|
std::vector<uint8> Data;
|
|
if (NRemoteCompiler::ESOK == NRemoteCompiler::CShaderSrv::Instance().GetShaderList(Data))
|
|
{
|
|
CryLogAlways("\nGet shader list Succeeded...\nStart Writing shader list to @user@\\cache\\shaders\\shaderlist.txt ...");
|
|
mfCloseShadersCache(0);
|
|
mfCloseShadersCache(1);
|
|
AZ::IO::HandleType fileHandle = gEnv->pCryPak->FOpen("@user@\\cache\\shaders\\shaderlist.txt", "w+b");
|
|
if (fileHandle != AZ::IO::InvalidHandle)
|
|
{
|
|
size_t writtenSoFar = 0;
|
|
size_t remaining = Data.size();
|
|
while (remaining)
|
|
{
|
|
writtenSoFar += gEnv->pCryPak->FWrite(Data.data() + writtenSoFar, 1, remaining, fileHandle);
|
|
remaining -= writtenSoFar;
|
|
}
|
|
gEnv->pCryPak->FClose(fileHandle);
|
|
CryLogAlways("\nFinished writing shader list to @user@\\cache\\shaders\\shaderlist.txt ...");
|
|
}
|
|
else
|
|
{
|
|
CryLogAlways("\nFailed writing shader list to @user@\\cache\\shaders\\shaderlist.txt ...");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CryLogAlways("\nGet shader list Failed...");
|
|
}
|
|
|
|
CParserBin::SetupForD3D11();
|
|
}
|
|
|
|
void CShaderMan::mfExportShaders()
|
|
{
|
|
}
|
|
|
|
void CShaderMan::mfOptimiseShaders(const char* szFolder, bool bForce)
|
|
{
|
|
CHWShader::mfFlushPendedShadersWait(-1);
|
|
|
|
float t0 = gEnv->pTimer->GetAsyncCurTime();
|
|
SShaderCache* pCache;
|
|
uint32 i;
|
|
|
|
std::vector<CCryNameR> Names;
|
|
mfGatherFilesList(szFolder, Names, 0, false);
|
|
|
|
SOptimiseStats Stats;
|
|
for (i = 0; i < Names.size(); i++)
|
|
{
|
|
const char* szName = Names[i].c_str();
|
|
constexpr AZStd::string_view userCache = "@usercache@/";
|
|
if (szName == userCache)
|
|
{
|
|
szName += userCache.size();
|
|
}
|
|
pCache = CHWShader::mfInitCache(szName, NULL, false, 0, false);
|
|
if (!pCache || !pCache->m_pRes[CACHE_USER])
|
|
{
|
|
continue;
|
|
}
|
|
SOptimiseStats _Stats;
|
|
CHWShader::mfOptimiseCacheFile(pCache, bForce, &_Stats);
|
|
Stats.nEntries += _Stats.nEntries;
|
|
Stats.nUniqueEntries += _Stats.nUniqueEntries;
|
|
Stats.nSizeCompressed += _Stats.nSizeCompressed;
|
|
Stats.nSizeUncompressed += _Stats.nSizeUncompressed;
|
|
Stats.nTokenDataSize += _Stats.nTokenDataSize;
|
|
Stats.nDirDataSize += _Stats.nDirDataSize;
|
|
pCache->Release();
|
|
}
|
|
|
|
float t1 = gEnv->pTimer->GetAsyncCurTime();
|
|
CryLog("-- All shaders combinations optimized in %.2f seconds", t1 - t0);
|
|
CryLog("-- Shader cache overall stats: Entries: %d, Unique Entries: %d, Size: %.3f, Compressed Size: %.3f, Token data size: %.3f, Directory Size: %.3f Mb", Stats.nEntries, Stats.nUniqueEntries, Stats.nSizeUncompressed / 1024.0f / 1024.0f, Stats.nSizeCompressed / 1024.0f / 1024.0f, Stats.nTokenDataSize / 1024.0f / 1024.0f, Stats.nDirDataSize / 1024.0f / 1024.0f);
|
|
}
|
|
|
|
struct SMgData
|
|
{
|
|
CCryNameTSCRC Name;
|
|
int nSize;
|
|
uint32 CRC;
|
|
uint32 flags;
|
|
byte* pData;
|
|
int nID;
|
|
byte bProcessed;
|
|
};
|
|
|
|
static int snCurListID;
|
|
typedef std::map<CCryNameTSCRC, SMgData> ShaderData;
|
|
typedef ShaderData::iterator ShaderDataItor;
|
|
|
|
static void sAddToList(SShaderCache* pCache, ShaderData& Data)
|
|
{
|
|
uint32 i;
|
|
CResFile* pRes = pCache->m_pRes[CACHE_USER];
|
|
ResDir* Dir = pRes->mfGetDirectory();
|
|
for (i = 0; i < Dir->size(); i++)
|
|
{
|
|
SDirEntry* pDE = &(*Dir)[i];
|
|
if (pDE->Name == CShaderMan::s_cNameHEAD)
|
|
{
|
|
continue;
|
|
}
|
|
ShaderDataItor it = Data.find(pDE->Name);
|
|
if (it == Data.end())
|
|
{
|
|
SMgData d;
|
|
d.nSize = pRes->mfFileRead(pDE);
|
|
SDirEntryOpen* pOE = pRes->mfGetOpenEntry(pDE);
|
|
assert(pOE);
|
|
if (!pOE)
|
|
{
|
|
continue;
|
|
}
|
|
d.flags = pDE->flags;
|
|
if (pDE->flags & RF_RES_$)
|
|
{
|
|
d.pData = new byte[d.nSize];
|
|
memcpy(d.pData, pOE->pData, d.nSize);
|
|
d.bProcessed = 0;
|
|
d.Name = pDE->Name;
|
|
d.CRC = 0;
|
|
d.nID = snCurListID++;
|
|
Data.insert(ShaderDataItor::value_type(d.Name, d));
|
|
continue;
|
|
}
|
|
if (d.nSize < sizeof(SShaderCacheHeaderItem))
|
|
{
|
|
assert(0);
|
|
continue;
|
|
}
|
|
d.pData = new byte[d.nSize];
|
|
memcpy(d.pData, pOE->pData, d.nSize);
|
|
SShaderCacheHeaderItem* pItem = (SShaderCacheHeaderItem*)d.pData;
|
|
d.bProcessed = 0;
|
|
d.Name = pDE->Name;
|
|
d.CRC = pItem->m_CRC32;
|
|
d.nID = snCurListID++;
|
|
Data.insert(ShaderDataItor::value_type(d.Name, d));
|
|
}
|
|
}
|
|
}
|
|
|
|
struct SNameData
|
|
{
|
|
CCryNameR Name;
|
|
bool bProcessed;
|
|
};
|
|
|
|
void CShaderMan::_MergeShaders()
|
|
{
|
|
float t0 = gEnv->pTimer->GetAsyncCurTime();
|
|
SShaderCache* pCache;
|
|
uint32 i, j;
|
|
|
|
std::vector<CCryNameR> NM;
|
|
std::vector<SNameData> Names;
|
|
mfGatherFilesList(m_ShadersMergeCachePath.c_str(), NM, 0, true);
|
|
for (i = 0; i < NM.size(); i++)
|
|
{
|
|
SNameData dt;
|
|
dt.bProcessed = false;
|
|
dt.Name = NM[i];
|
|
Names.push_back(dt);
|
|
}
|
|
|
|
uint32 CRC32 = 0;
|
|
for (i = 0; i < Names.size(); i++)
|
|
{
|
|
if (Names[i].bProcessed)
|
|
{
|
|
continue;
|
|
}
|
|
Names[i].bProcessed = true;
|
|
const char* szNameA = Names[i].Name.c_str();
|
|
iLog->Log(" Merging shader resource '%s'...", szNameA);
|
|
char szDrv[16], szDir[256], szName[256], szExt[32], szName1[256], szName2[256];
|
|
#ifdef AZ_COMPILER_MSVC
|
|
_splitpath_s(szNameA, szDrv, szDir, szName, szExt);
|
|
#else
|
|
_splitpath(szNameA, szDrv, szDir, szName, szExt);
|
|
#endif
|
|
azsprintf(szName1, "%s%s", szName, szExt);
|
|
uint32 nLen = strlen(szName1);
|
|
pCache = CHWShader::mfInitCache(szNameA, NULL, false, CRC32, false);
|
|
SResFileLookupData* pData;
|
|
if (pCache->m_pRes[CACHE_USER] && (pData = pCache->m_pRes[CACHE_USER]->GetLookupData(false, 0, 0)))
|
|
{
|
|
CRC32 = pData->m_CRC32;
|
|
}
|
|
else
|
|
if (pCache->m_pRes[CACHE_READONLY] && (pData = pCache->m_pRes[CACHE_READONLY]->GetLookupData(false, 0, 0)))
|
|
{
|
|
CRC32 = pData->m_CRC32;
|
|
}
|
|
else
|
|
{
|
|
assert(0);
|
|
}
|
|
ShaderData Data;
|
|
snCurListID = 0;
|
|
sAddToList(pCache, Data);
|
|
SAFE_RELEASE(pCache);
|
|
for (j = i + 1; j < Names.size(); j++)
|
|
{
|
|
if (Names[j].bProcessed)
|
|
{
|
|
continue;
|
|
}
|
|
const char* szNameB = Names[j].Name.c_str();
|
|
#ifdef AZ_COMPILER_MSVC
|
|
_splitpath_s(szNameB, szDrv, szDir, szName, szExt);
|
|
#else
|
|
_splitpath(szNameB, szDrv, szDir, szName, szExt);
|
|
#endif
|
|
azsprintf(szName2, "%s%s", szName, szExt);
|
|
if (!azstricmp(szName1, szName2))
|
|
{
|
|
Names[j].bProcessed = true;
|
|
SShaderCache* pCache1 = CHWShader::mfInitCache(szNameB, NULL, false, 0, false);
|
|
pData = pCache1->m_pRes[CACHE_USER]->GetLookupData(false, 0, 0);
|
|
assert(pData && pData->m_CRC32 == CRC32);
|
|
if (!pData || pData->m_CRC32 != CRC32)
|
|
{
|
|
Warning("WARNING: CRC mismatch for %s", szNameB);
|
|
}
|
|
sAddToList(pCache1, Data);
|
|
SAFE_RELEASE(pCache1);
|
|
}
|
|
}
|
|
char szDest[256];
|
|
cry_strcpy(szDest, m_ShadersCache.c_str());
|
|
const char* p = &szNameA[strlen(szNameA) - nLen - 2];
|
|
while (*p != '/' && *p != '\\')
|
|
{
|
|
p--;
|
|
}
|
|
cry_strcat(szDest, p + 1);
|
|
pCache = CHWShader::mfInitCache(szDest, NULL, true, CRC32, false);
|
|
CResFile* pRes = pCache->m_pRes[CACHE_USER];
|
|
pRes->mfClose();
|
|
pRes->mfOpen(RA_CREATE, &gRenDev->m_cEF.m_ResLookupDataMan[CACHE_USER]);
|
|
|
|
SResFileLookupData* pLookup = pRes->GetLookupData(true, CRC32, FX_CACHE_VER);
|
|
pRes->mfFlush();
|
|
|
|
int nDeviceShadersCounter = 0x10000000;
|
|
ShaderDataItor it;
|
|
for (it = Data.begin(); it != Data.end(); it++)
|
|
{
|
|
SMgData* pD = &it->second;
|
|
SDirEntry de;
|
|
de.Name = pD->Name;
|
|
de.size = pD->nSize;
|
|
de.flags = pD->flags;
|
|
if (pD->flags & RF_RES_$)
|
|
{
|
|
de.flags &= ~RF_COMPRESS;
|
|
}
|
|
else
|
|
{
|
|
de.flags |= RF_COMPRESS;
|
|
de.offset = nDeviceShadersCounter++;
|
|
}
|
|
byte* pNew = new byte[de.size];
|
|
memcpy(pNew, pD->pData, pD->nSize);
|
|
de.flags |= RF_TEMPDATA;
|
|
pRes->mfFileAdd(&de);
|
|
SDirEntryOpen* pOE = pRes->mfOpenEntry(&de);
|
|
pOE->pData = pNew;
|
|
}
|
|
for (it = Data.begin(); it != Data.end(); it++)
|
|
{
|
|
SMgData* pD = &it->second;
|
|
delete [] pD->pData;
|
|
}
|
|
Data.clear();
|
|
pRes->mfFlush();
|
|
iLog->Log(" ...%d result items...", pRes->mfGetNumFiles());
|
|
pCache->Release();
|
|
}
|
|
|
|
mfOptimiseShaders(gRenDev->m_cEF.m_ShadersCache.c_str(), true);
|
|
|
|
float t1 = gEnv->pTimer->GetAsyncCurTime();
|
|
CryLog("All shaders files merged in %.2f seconds", t1 - t0);
|
|
}
|
|
|
|
void CShaderMan::mfMergeShaders()
|
|
{
|
|
CHWShader::mfFlushPendedShadersWait(-1);
|
|
|
|
CParserBin::SetupForD3D11();
|
|
_MergeShaders();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CShaderMan::CheckAllFilesAreWritable(const char* szDir) const
|
|
{
|
|
#if (defined(WIN32) || defined(WIN64))
|
|
assert(szDir);
|
|
|
|
auto pack = gEnv->pCryPak;
|
|
assert(pack);
|
|
|
|
string sPathWithFilter = string(szDir) + "/*";
|
|
|
|
// Search files that match filter specification.
|
|
AZ::IO::ArchiveFileIterator handle;
|
|
if (handle = pack->FindFirst(sPathWithFilter.c_str()); handle)
|
|
{
|
|
do
|
|
{
|
|
if ((handle.m_fileDesc.nAttrib & AZ::IO::FileDesc::Attribute::Subdirectory) != AZ::IO::FileDesc::Attribute::Subdirectory)
|
|
{
|
|
string fullpath = string(szDir) + "/" + string(handle.m_filename.data(), handle.m_filename.size());
|
|
|
|
AZ::IO::HandleType outFileHandle = pack->FOpen(fullpath.c_str(), "rb");
|
|
if (outFileHandle == AZ::IO::InvalidHandle)
|
|
{
|
|
handle = pack->FindNext(handle);
|
|
continue;
|
|
}
|
|
if (pack->IsInPak(outFileHandle))
|
|
{
|
|
pack->FClose(outFileHandle);
|
|
handle = pack->FindNext(handle);
|
|
continue;
|
|
}
|
|
pack->FClose(outFileHandle);
|
|
|
|
outFileHandle = pack->FOpen(fullpath.c_str(), "ab");
|
|
|
|
if (outFileHandle != AZ::IO::InvalidHandle)
|
|
{
|
|
pack->FClose(outFileHandle);
|
|
}
|
|
else
|
|
{
|
|
gEnv->pLog->LogError("ERROR: Shader cache is not writable (file: '%s')", fullpath.c_str());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
handle = pack->FindNext(handle);
|
|
} while (handle);
|
|
|
|
pack->FindClose(handle);
|
|
|
|
gEnv->pLog->LogToFile("Shader cache directory '%s' was successfully tested for being writable", szDir);
|
|
}
|
|
else
|
|
{
|
|
CryLog("Shader cache directory '%s' does not exist", szDir);
|
|
}
|
|
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
#endif // CONSOLE
|
|
|
|
bool CShaderMan::mfPreloadBinaryShaders()
|
|
{
|
|
LOADING_TIME_PROFILE_SECTION;
|
|
AZ_TRACE_METHOD();
|
|
// don't preload binary shaders if we are in editing mode
|
|
if IsCVarConstAccess(constexpr) (CRenderer::CV_r_shadersediting)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// don't load all binary shaders twice
|
|
if (m_Bin.m_bBinaryShadersLoaded)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool bFound = iSystem->GetIPak()->LoadPakToMemory("Engine/ShadersBin.pak", AZ::IO::IArchive::eInMemoryPakLocale_CPU);
|
|
if (!bFound)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
#ifndef _RELEASE
|
|
// also load shaders pak file to memory because shaders are also read, when data not found in bin, and to check the CRC
|
|
// of the source shaders against the binary shaders in non release mode
|
|
iSystem->GetIPak()->LoadPakToMemory("Engine/Shaders.pak", AZ::IO::IArchive::eInMemoryPakLocale_CPU);
|
|
#endif
|
|
|
|
AZStd::string allFilesPath = m_ShadersCache + "/*";
|
|
|
|
AZ::IO::ArchiveFileIterator handle = gEnv->pCryPak->FindFirst (allFilesPath.c_str());
|
|
if (!handle)
|
|
{
|
|
return false;
|
|
}
|
|
std::vector<string> FilesCFX;
|
|
std::vector<string> FilesCFI;
|
|
|
|
do
|
|
{
|
|
if (gEnv->pSystem && gEnv->pSystem->IsQuitting())
|
|
{
|
|
return false;
|
|
}
|
|
if (handle.m_filename.front() == '.')
|
|
{
|
|
continue;
|
|
}
|
|
if ((handle.m_fileDesc.nAttrib & AZ::IO::FileDesc::Attribute::Subdirectory) == AZ::IO::FileDesc::Attribute::Subdirectory)
|
|
{
|
|
continue;
|
|
}
|
|
const char* szExt = fpGetExtension(handle.m_filename.data());
|
|
if (!azstricmp(szExt, ".cfib"))
|
|
{
|
|
FilesCFI.emplace_back(handle.m_filename.data(), handle.m_filename.size());
|
|
}
|
|
else
|
|
if (!azstricmp(szExt, ".cfxb"))
|
|
{
|
|
FilesCFX.emplace_back(handle.m_filename.data(), handle.m_filename.size());
|
|
}
|
|
} while (handle = gEnv->pCryPak->FindNext(handle));
|
|
|
|
if (FilesCFX.size() + FilesCFI.size() > MAX_FXBIN_CACHE)
|
|
{
|
|
SShaderBin::s_nMaxFXBinCache = FilesCFX.size() + FilesCFI.size();
|
|
}
|
|
uint32 i;
|
|
char sName[256];
|
|
|
|
{
|
|
LOADING_TIME_PROFILE_SECTION_NAMED("CShaderMan::mfPreloadBinaryShaders(): FilesCFI");
|
|
for (i = 0; i < FilesCFI.size(); i++)
|
|
{
|
|
if (gEnv->pSystem && gEnv->pSystem->IsQuitting())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const string& file = FilesCFI[i];
|
|
cry_strcpy(sName, file.c_str());
|
|
fpStripExtension(sName, sName);
|
|
SShaderBin* pBin = m_Bin.GetBinShader(sName, true, 0);
|
|
AZ_Error("Rendering", pBin, "Error pre-loading binary shader %s", file.c_str());
|
|
}
|
|
}
|
|
|
|
{
|
|
LOADING_TIME_PROFILE_SECTION_NAMED("CShaderMan::mfPreloadBinaryShaders(): FilesCFX");
|
|
for (i = 0; i < FilesCFX.size(); i++)
|
|
{
|
|
if (gEnv->pSystem && gEnv->pSystem->IsQuitting())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const string& file = FilesCFX[i];
|
|
cry_strcpy(sName, file.c_str());
|
|
fpStripExtension(sName, sName);
|
|
SShaderBin* pBin = m_Bin.GetBinShader(sName, false, 0);
|
|
AZ_Error("Rendering", pBin, "Error pre-loading binary shader %s", file.c_str());
|
|
}
|
|
}
|
|
|
|
gEnv->pCryPak->FindClose (handle);
|
|
|
|
// Unload pak from memory.
|
|
iSystem->GetIPak()->LoadPakToMemory("Engine/ShadersBin.pak", AZ::IO::IArchive::eInMemoryPakLocale_Unload);
|
|
|
|
#ifndef _RELEASE
|
|
iSystem->GetIPak()->LoadPakToMemory("Engine/Shaders.pak", AZ::IO::IArchive::eInMemoryPakLocale_Unload);
|
|
#endif
|
|
|
|
m_Bin.m_bBinaryShadersLoaded = true;
|
|
|
|
return SShaderBin::s_nMaxFXBinCache > 0;
|
|
}
|