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.
o3de/Code/CryEngine/RenderDll/Common/PostProcess/PostProcess.cpp

674 lines
21 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 "PostEffects.h"
#include <I3DEngine.h>
void CParamBool::SetParam(float fParam, [[maybe_unused]] bool bForceValue)
{
const int threadID = gRenDev->m_pRT ? gRenDev->m_pRT->GetThreadList() : 0;
CParamBoolThreadSafeData* pThreadSafeData = &m_threadSafeData[threadID];
pThreadSafeData->bParam = (fParam) ? true : false;
pThreadSafeData->bSetThisFrame = true;
}
float CParamBool::GetParam()
{
const int threadID = gRenDev->m_pRT ? gRenDev->m_pRT->GetThreadList() : 0;
return static_cast<float>(m_threadSafeData[threadID].bParam);
}
void CParamBool::SyncMainWithRender()
{
CParamBoolThreadSafeData* pFillData = &m_threadSafeData[gRenDev->m_RP.m_nFillThreadID];
const bool bIsMultiThreaded = (gRenDev->m_pRT) ? (gRenDev->m_pRT->IsMultithreaded()) : false;
CParamBoolThreadSafeData* pProcessData = NULL;
if (bIsMultiThreaded)
{
// If value is set on render thread, then this should override main thread value
pProcessData = &m_threadSafeData[gRenDev->m_RP.m_nProcessThreadID];
if (pProcessData->bSetThisFrame)
{
pFillData->bParam = pProcessData->bParam;
}
}
// Reset set value
pFillData->bSetThisFrame = false;
if (bIsMultiThreaded)
{
// Copy fill data into process data
memcpy(pProcessData, pFillData, sizeof(CParamBoolThreadSafeData));
}
}
void CParamInt::SetParam(float fParam, [[maybe_unused]] bool bForceValue)
{
const int threadID = gRenDev->m_pRT ? gRenDev->m_pRT->GetThreadList() : 0;
CParamIntThreadSafeData* pThreadSafeData = &m_threadSafeData[threadID];
pThreadSafeData->nParam = static_cast<int>(fParam);
pThreadSafeData->bSetThisFrame = true;
}
float CParamInt::GetParam()
{
const int threadID = gRenDev->m_pRT ? gRenDev->m_pRT->GetThreadList() : 0;
return static_cast<float>(m_threadSafeData[threadID].nParam);
}
void CParamInt::SyncMainWithRender()
{
CParamIntThreadSafeData* pFillData = &m_threadSafeData[gRenDev->m_RP.m_nFillThreadID];
const bool bIsMultiThreaded = (gRenDev->m_pRT) ? (gRenDev->m_pRT->IsMultithreaded()) : false;
CParamIntThreadSafeData* pProcessData = NULL;
if (bIsMultiThreaded)
{
// If value is set on render thread, then this should override main thread value
pProcessData = &m_threadSafeData[gRenDev->m_RP.m_nProcessThreadID];
if (pProcessData->bSetThisFrame)
{
pFillData->nParam = pProcessData->nParam;
}
}
// Reset set value
pFillData->bSetThisFrame = false;
if (bIsMultiThreaded)
{
// Copy fill data into process data
memcpy(pProcessData, pFillData, sizeof(CParamIntThreadSafeData));
}
}
void CParamFloat::SetParam(float fParam, [[maybe_unused]] bool bForceValue)
{
const int threadID = gRenDev->m_pRT ? gRenDev->m_pRT->GetThreadList() : 0;
CParamFloatThreadSafeData* pThreadSafeData = &m_threadSafeData[threadID];
pThreadSafeData->fParam = fParam;
pThreadSafeData->bSetThisFrame = true;
}
float CParamFloat::GetParam()
{
const int threadID = gRenDev->m_pRT ? gRenDev->m_pRT->GetThreadList() : 0;
return m_threadSafeData[threadID].fParam;
}
void CParamFloat::SyncMainWithRender()
{
// The Effect params can be set/get from both threads, accumulate and sync data here
CParamFloatThreadSafeData* pFillData = &m_threadSafeData[gRenDev->m_RP.m_nFillThreadID];
const bool bIsMultiThreaded = (gRenDev->m_pRT) ? (gRenDev->m_pRT->IsMultithreaded()) : false;
CParamFloatThreadSafeData* pProcessData = NULL;
if (bIsMultiThreaded)
{
// If value is set on render thread, then this should override main thread value
pProcessData = &m_threadSafeData[gRenDev->m_RP.m_nProcessThreadID];
if (pProcessData->bSetThisFrame)
{
pFillData->fParam = pProcessData->fParam;
}
}
// Reset set value
pFillData->bSetThisFrame = false;
if (bIsMultiThreaded)
{
// Copy fill data into process data
memcpy(pProcessData, pFillData, sizeof(CParamFloatThreadSafeData));
}
}
void CParamVec4::SetParamVec4(const Vec4& vParam, [[maybe_unused]] bool bForceValue)
{
const int threadID = gRenDev->m_pRT ? gRenDev->m_pRT->GetThreadList() : 0;
CParamVec4ThreadSafeData* pThreadSafeData = &m_threadSafeData[threadID];
pThreadSafeData->vParam = vParam;
pThreadSafeData->bSetThisFrame = true;
}
Vec4 CParamVec4::GetParamVec4()
{
const int threadID = gRenDev->m_pRT ? gRenDev->m_pRT->GetThreadList() : 0;
return m_threadSafeData[threadID].vParam;
}
void CParamVec4::SyncMainWithRender()
{
// The Effect params can be set/get from both threads, accumulate and sync data here
CParamVec4ThreadSafeData* pFillData = &m_threadSafeData[gRenDev->m_RP.m_nFillThreadID];
const bool bIsMultiThreaded = (gRenDev->m_pRT) ? (gRenDev->m_pRT->IsMultithreaded()) : false;
CParamVec4ThreadSafeData* pProcessData = NULL;
if (bIsMultiThreaded)
{
// If value is set on render thread, then this should override main thread value
pProcessData = &m_threadSafeData[gRenDev->m_RP.m_nProcessThreadID];
if (pProcessData->bSetThisFrame)
{
pFillData->vParam = pProcessData->vParam;
}
}
// Reset set value
pFillData->bSetThisFrame = false;
if (bIsMultiThreaded)
{
// Copy fill data into process data
memcpy(pProcessData, pFillData, sizeof(CParamVec4ThreadSafeData));
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
#if !defined(_RELEASE)
static void SetPostEffectParamF(IConsoleCmdArgs* pArgs)
{
if (pArgs->GetArgCount() < 3)
{
return;
}
bool bForceValue = false;
if (pArgs->GetArgCount() > 3)
{
const int iForceValue = (int)atoi(pArgs->GetArg(3));
if (iForceValue != 0)
{
bForceValue = true;
}
}
const char* szPostEffectParamName = pArgs->GetArg(1);
const float fValue = (float)atof(pArgs->GetArg(2));
I3DEngine* pEngine = gEnv->p3DEngine;
pEngine->SetPostEffectParam(szPostEffectParamName, fValue, bForceValue);
}
static void GetPostEffectParamF(IConsoleCmdArgs* pArgs)
{
if (pArgs->GetArgCount() < 2)
{
return;
}
const char* szPostEffectParamName = pArgs->GetArg(1);
I3DEngine* pEngine = gEnv->p3DEngine;
float fValue = 0.0f;
pEngine->GetPostEffectParam(szPostEffectParamName, fValue);
CryLogAlways("\nPost effect param value: %f", fValue);
}
#endif // !defined(_RELEASE)
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
int CPostEffectsMgr::Init()
{
m_bPostReset = false;
ClearCache();
// Initialize the CRC table
// This is the official polynomial used by CRC-32
// in PKZip, WinZip and Ethernet.
unsigned int ulPolynomial = 0x04c11db7;
// 256 values representing ASCII character codes.
for (int i = 0; i <= 0xFF; i++)
{
m_nCRC32Table[i] = CRC32Reflect(i, 8) << 24;
for (int j = 0; j < 8; j++)
{
m_nCRC32Table[i] = (m_nCRC32Table[i] << 1) ^ (m_nCRC32Table[i] & (1 << 31) ? ulPolynomial : 0);
}
m_nCRC32Table[i] = CRC32Reflect(m_nCRC32Table[i], 32);
} //i
// Register default parameters
AddParamFloat("Global_Brightness", m_pBrightness, 1.0f); // brightness
AddParamFloat("Global_Contrast", m_pContrast, 1.0f); // contrast
AddParamFloat("Global_Saturation", m_pSaturation, 1.0f); // saturation
AddParamFloat("Global_ColorC", m_pColorC, 0.0f); // cyan amount
AddParamFloat("Global_ColorY", m_pColorY, 0.0f); // yellow amount
AddParamFloat("Global_ColorM", m_pColorM, 0.0f); // magenta amount
AddParamFloat("Global_ColorK", m_pColorK, 0.0f); // darkness amount
AddParamFloat("Global_ColorHue", m_pColorHue, 0.0f); // image hue rotation
// User parameters
AddParamFloat("Global_User_Brightness", m_pUserBrightness, 1.0f); // brightness
AddParamFloat("Global_User_Contrast", m_pUserContrast, 1.0f); // contrast
AddParamFloat("Global_User_Saturation", m_pUserSaturation, 1.0f); // saturation
AddParamFloat("Global_User_ColorC", m_pUserColorC, 0.0f); // cyan amount
AddParamFloat("Global_User_ColorY", m_pUserColorY, 0.0f); // yellow amount
AddParamFloat("Global_User_ColorM", m_pUserColorM, 0.0f); // magenta amount
AddParamFloat("Global_User_ColorK", m_pUserColorK, 0.0f); // darkness amount
AddParamFloat("Global_User_ColorHue", m_pUserColorHue, 0.0f); // image hue rotation
AddParamFloat("Global_User_HDRBloom", m_UserHDRBloom, 0.f); // bloom amount
// Register all post processes
AddEffect(CSceneSnow);
AddEffect(CSceneRain);
AddEffect(CSunShafts);
AddEffect(CDepthOfField);
AddEffect(CMotionBlur);
AddEffect(CUnderwaterGodRays);
AddEffect(CVolumetricScattering);
AddEffect(CRainDrops);
AddEffect(CWaterDroplets);
AddEffect(CWaterFlow);
AddEffect(CScreenFrost);
AddEffect(CAlienInterference);
AddEffect(CFlashBang);
AddEffect(CFilterSharpening);
AddEffect(CFilterBlurring);
AddEffect(CColorGrading);
AddEffect(CHudSilhouettes);
AddEffect(CImageGhosting);
AddEffect(CWaterRipples);
AddEffect(CWaterVolume);
AddEffect(CPostAA);
AddEffect(CFilterKillCamera);
AddEffect(CUberGamePostProcess);
AddEffect(CSoftAlphaTest);
AddEffect(CScreenBlood);
AddEffect(CPost3DRenderer);
AddEffect(CGhostVision);
AddEffect(ScreenFader);
// Sort all post effects by ID
std::sort(m_pEffects.begin(), m_pEffects.end(), SortEffectsByID);
// Initialize all post process techniques
std::for_each(m_pEffects.begin(), m_pEffects.end(), SContainerPostEffectInitialize());
m_bCreated = false;
// Initialize parameters
StringEffectMapItor pItor = m_pNameIdMapGen.begin(), pEnd = m_pNameIdMapGen.end();
for (; pItor != pEnd; ++pItor)
{
m_pNameIdMap.insert(KeyEffectMapItor::value_type(GetCRC(pItor->first.c_str()), pItor->second));
}
m_pNameIdMapGen.clear();
#if !defined(_RELEASE)
REGISTER_COMMAND("r_setposteffectparamf", SetPostEffectParamF, VF_CHEAT, "Sets post effect param (float)n"
"Usage: r_setposteffectparamf [posteffectparamname, value, forceValue(OPTIONAL)]\n"
"Example: r_setposteffectparamf HUD3D_FOV 35.0 (Doesn't force value)\n"
"Example: r_setposteffectparamf HUD3D_FOV 35.0 1 (Forces value)\n");
REGISTER_COMMAND("r_getposteffectparamf", GetPostEffectParamF, VF_CHEAT, "Outputs post effect param value (float) to log"
"Usage: r_setposteffectparamf [posteffectparamname]\n"
"Example: r_getposteffectparamf HUD3D_FOV\n");
#endif
// TESTING
static int r_3MonHack;
static float r_3MonHackHUDFOVX;
static float r_3MonHackHUDFOVY;
static float r_3MonHackLeftCGFOffsetX;
static float r_3MonHackRightCGFOffsetX;
REGISTER_CVAR(r_3MonHack, 0, VF_CHEAT | VF_CHEAT_NOCHECK, "Enables 3 monitor hack hud in center");
REGISTER_CVAR(r_3MonHackHUDFOVX, 28, VF_CHEAT | VF_CHEAT_NOCHECK, "3 monitor hack hud in center - X FOV");
REGISTER_CVAR(r_3MonHackHUDFOVY, 60, VF_CHEAT | VF_CHEAT_NOCHECK, "3 monitor hack hud in center - Y FOV");
REGISTER_CVAR(r_3MonHackLeftCGFOffsetX, 0.93f, VF_CHEAT | VF_CHEAT_NOCHECK, "3 monitor hack hud in center - Adds position offset in X direction to all left CGF planes");
REGISTER_CVAR(r_3MonHackRightCGFOffsetX, -0.93f, VF_CHEAT | VF_CHEAT_NOCHECK, "3 monitor hack hud in center - Adds position offset in X direction to all right CGF planes");
return 1;
}
void CPostEffectsMgr::CreateResources()
{
// Initialize all post process techniques
if (!m_bCreated)
{
std::for_each(m_pEffects.begin(), m_pEffects.end(), SContainerPostEffectCreateResources());
}
m_bCreated = true;
}
void CPostEffectsMgr::ReleaseResources()
{
if (m_bCreated)
{
std::for_each(m_pEffects.begin(), m_pEffects.end(), container_object_safe_release());
}
#ifndef _RELEASE
ClearDebugInfo();
#endif
m_bCreated = false;
}
void CPostEffectsMgr::Release()
{
// Free all resources
ClearCache();
std::for_each(m_pNameIdMap.begin(), m_pNameIdMap.end(), SContainerKeyEffectParamDelete());
m_pNameIdMap.clear();
std::for_each(m_pEffects.begin(), m_pEffects.end(), container_object_safe_release());
std::for_each(m_pEffects.begin(), m_pEffects.end(), container_object_safe_delete());
m_pEffects.clear();
m_bCreated = false;
#if !defined(_RELEASE)
IConsole* pConsole = gEnv->pConsole;
if (pConsole)
{
pConsole->RemoveCommand("r_setposteffectparamf");
pConsole->RemoveCommand("r_getposteffectparamf");
}
#endif
}
void CPostEffectsMgr::Reset(bool bOnSpecChange)
{
ClearCache();
m_pBrightness->ResetParam(1.0f);
m_pContrast->ResetParam(1.0f);
m_pSaturation->ResetParam(1.0f);
m_pColorC->ResetParam(0.0f);
m_pColorY->ResetParam(0.0f);
m_pColorM->ResetParam(0.0f);
m_pColorK->ResetParam(0.0f);
m_pColorHue->ResetParam(0.0f);
m_pUserBrightness->ResetParam(1.0f);
m_pUserContrast->ResetParam(1.0f);
m_pUserSaturation->ResetParam(1.0f);
m_pUserColorC->ResetParam(0.0f);
m_pUserColorY->ResetParam(0.0f);
m_pUserColorM->ResetParam(0.0f);
m_pUserColorK->ResetParam(0.0f);
m_pUserColorHue->ResetParam(0.0f);
if (bOnSpecChange)
{
std::for_each(m_pEffects.begin(), m_pEffects.end(), SContainerPostEffectResetOnSpecChange());
}
else
{
std::for_each(m_pEffects.begin(), m_pEffects.end(), SContainerPostEffectReset());
}
}
int32 CPostEffectsMgr::GetEffectID(const char* pEffectName)
{
int32 effectID = ePFX_Invalid;
CPostEffectsMgr* pPostMgr = PostEffectMgr();
for (CPostEffectItor pItor = pPostMgr->GetEffects().begin(), pItorEnd = pPostMgr->GetEffects().end(); pItor != pItorEnd; ++pItor)
{
CPostEffect* pCurrEffect = (*pItor);
if (strcmp(pEffectName, pCurrEffect->GetName()) == 0)
{
effectID = pCurrEffect->GetID();
break;
}
}
return effectID;
}
void CPostEffectsMgr::OnLostDevice()
{
std::for_each(m_pEffects.begin(), m_pEffects.end(), SContainerPostEffectOnLostDevice());
}
void CPostEffectsMgr::OnBeginFrame()
{
std::for_each(m_pEffects.begin(), m_pEffects.end(), SContainerPostEffectOnBeginFrame());
}
void CPostEffectsMgr::SyncMainWithRender()
{
KeyEffectMapItor iter;
for (iter = m_pNameIdMap.begin(); iter != m_pNameIdMap.end(); iter++)
{
iter->second->SyncMainWithRender();
}
}
// Used only when creating the crc table
uint32 CPostEffectsMgr::CRC32Reflect(uint32 ref, char ch)
{
uint32 value = 0;
// Swap bit 0 for bit 7
// bit 1 for bit 6, etc.
for (int i = 1; i < (ch + 1); i++)
{
if (ref & 1)
{
value |= 1 << (ch - i);
}
ref >>= 1;
}
return value;
}
uint32 CPostEffectsMgr::GetCRC(const char* pszName)
{
if (!pszName)
{
assert(false && "CPostEffectsMgr::GetCRC() invalid string passed");
return 0;
}
// Once the lookup table has been filled in by the constructor,
// this function creates all CRCs using only the lookup table.
// Be sure to use unsigned variables, because negative values introduce high bits where zero bits are required.
const char* szPtr = pszName;
uint32 ulCRC = 0xffffffff; // Start out with all bits set high.
unsigned char c;
while (*szPtr)
{
c = *szPtr++;
if (c >= 'a' && c <= 'z')
{
c -= 32; //convert to uppercase
}
ulCRC = (ulCRC >> 8) ^ m_nCRC32Table[(ulCRC & 0xFF) ^ c];
}
// Exclusive OR the result with the beginning value...avoids the % operator
return ulCRC ^ 0xffffffff;
}
CEffectParam* CPostEffectsMgr::GetByName(const char* pszParam)
{
assert(pszParam || "mfGetByName: null FX name");
uint32 nCurrKey = GetCRC(pszParam);
int nThreadID = gRenDev->m_pRT ? gRenDev->m_pRT->GetThreadList() : 0;
// cache per-thread
ParamCache* pCache = &m_pParamCache[nThreadID];
// Check cache first
if (nCurrKey == pCache->m_nKey && pCache->m_pParam)
{
if (CRenderer::CV_r_PostProcess == 3)
{
m_pEffectParamsUpdated.insert(StringEffectMapItor::value_type(pszParam, pCache->m_pParam));
}
return pCache->m_pParam;
}
KeyEffectMapItor pItor = m_pNameIdMap.find(nCurrKey);
if (pItor != m_pNameIdMap.end())
{
pCache->m_pParam = pItor->second;
pCache->m_nKey = nCurrKey;
if (CRenderer::CV_r_PostProcess == 3)
{
m_pEffectParamsUpdated.insert(StringEffectMapItor::value_type(pszParam, pCache->m_pParam));
}
return pCache->m_pParam;
}
return 0;
}
float CPostEffectsMgr::GetByNameF(const char* pszParam)
{
CEffectParam* pParam = GetByName(pszParam);
if (pParam)
{
return pParam->GetParam();
}
return 0.0f;
}
Vec4 CPostEffectsMgr::GetByNameVec4(const char* pszParam)
{
CEffectParam* pParam = GetByName(pszParam);
if (pParam)
{
return pParam->GetParamVec4();
}
return Vec4(0, 0, 0, 0);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
int CPostEffectsMgr::SortEffectsByID(const CPostEffect* p1, const CPostEffect* p2)
{
return (p1->GetID() < p2->GetID());
}
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
int CParamTexture::Create(const char* pszFileName)
{
if (!pszFileName || pszFileName[0] == '\0')
{
return 0;
}
const int threadID = gRenDev->m_pRT ? gRenDev->m_pRT->GetThreadList() : 0;
CParamTextureThreadSafeData* pThreadSafeData = &m_threadSafeData[threadID];
if (pThreadSafeData->pTexParam)
{
// check if texture is same
if (!azstricmp(pThreadSafeData->pTexParam->GetName(), pszFileName))
{
return 0;
}
// release texture if required
SAFE_RELEASE(pThreadSafeData->pTexParam);
}
pThreadSafeData->pTexParam = CTexture::ForName(pszFileName, FT_DONT_STREAM, eTF_Unknown);
pThreadSafeData->bSetThisFrame = true;
assert(pThreadSafeData->pTexParam || "CParamTexture.Create: texture not found!");
return 1;
}
const char* CParamTexture::GetParamString() const
{
const int threadID = gRenDev->m_pRT ? gRenDev->m_pRT->GetThreadList() : 0;
const CTexture* texture = m_threadSafeData[threadID].pTexParam;
return texture ? texture->GetName() : "";
}
void CParamTexture::Release()
{
CParamTextureThreadSafeData* pFillData = &m_threadSafeData[gRenDev->m_RP.m_nFillThreadID];
const bool bIsMultiThreaded = (gRenDev->m_pRT) ? (gRenDev->m_pRT->IsMultithreaded()) : false;
if (bIsMultiThreaded)
{
CParamTextureThreadSafeData* pProcessData = &m_threadSafeData[gRenDev->m_RP.m_nProcessThreadID];
if (pProcessData->pTexParam != pFillData->pTexParam)
{
SAFE_RELEASE(pProcessData->pTexParam);
pProcessData->bSetThisFrame = true;
}
}
SAFE_RELEASE(pFillData->pTexParam);
pFillData->bSetThisFrame = true;
}
void CParamTexture::SyncMainWithRender()
{
CParamTextureThreadSafeData* pFillData = &m_threadSafeData[gRenDev->m_RP.m_nFillThreadID];
const bool bIsMultiThreaded = (gRenDev->m_pRT) ? (gRenDev->m_pRT->IsMultithreaded()) : false;
CParamTextureThreadSafeData* pProcessData = NULL;
if (bIsMultiThreaded)
{
// If value is set on render thread, then this should override main thread value
pProcessData = &m_threadSafeData[gRenDev->m_RP.m_nProcessThreadID];
if (pProcessData->bSetThisFrame)
{
if (pFillData->bSetThisFrame)
{
// If the main thread also set a texture on the same frame (highly unlikely), then release this texture
// 1st before overriding it with the texture set from the render thread
SAFE_RELEASE(pFillData->pTexParam);
}
pFillData->pTexParam = pProcessData->pTexParam;
}
}
// Reset set value
pFillData->bSetThisFrame = false;
if (bIsMultiThreaded)
{
// Copy fill data into process data
memcpy(pProcessData, pFillData, sizeof(CParamTextureThreadSafeData));
}
}
CPostEffectsMgr* PostEffectMgr()
{
return gRenDev->m_pPostProcessMgr;
}