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/XRenderD3D9/D3DHDRRender.cpp

2232 lines
84 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 "DriverD3D.h"
#include "D3DPostProcess.h"
#include "I3DEngine.h"
#include "../Common/Textures/TextureManager.h"
#include "../Common/ReverseDepth.h"
#include "CryCommon/FrameProfiler.h"
enum class ToneMapOperators : int
{
FilmicCurveUC2 = 0,
Linear = 1,
Exponential = 2,
Reinhard = 3,
FilmicCurveALU = 4
};
enum class Exposuretype : int
{
Auto = 0, //Any other variations of AUTO will go here.
Manual = 1
};
// render targets info - first gather list of hdr targets, sort by size and create after
struct SRenderTargetInfo
{
SRenderTargetInfo()
: nWidth(0)
, nHeight(0)
, cClearColor(Clr_Empty)
, Format(eTF_Unknown)
, nFlags(0)
, lplpStorage(0)
, nPitch(0)
, fPriority(0.0f)
, nCustomID(0)
{};
uint32 nWidth;
uint32 nHeight;
ColorF cClearColor;
ETEX_Format Format;
uint32 nFlags;
CTexture** lplpStorage;
char szName[64];
uint32 nPitch;
float fPriority;
int32 nCustomID;
};
struct RenderTargetSizeSort
{
bool operator()(const SRenderTargetInfo& drtStart, const SRenderTargetInfo& drtEnd) { return (drtStart.nPitch * drtStart.fPriority) > (drtEnd.nPitch * drtEnd.fPriority); }
};
class CHDRPostProcess
{
public:
CHDRPostProcess()
{
nTexStateLinear = CTexture::GetTexState(STexState(FILTER_LINEAR, true));
nTexStateLinearWrap = CTexture::GetTexState(STexState(FILTER_LINEAR, false));
nTexStatePoint = CTexture::GetTexState(STexState(FILTER_POINT, true));
nTexStatePointWrap = CTexture::GetTexState(STexState(FILTER_POINT, false));
m_bHiQuality = false;
m_shHDR = 0;
m_shHDRDolbyMetadataPass0 = nullptr;
m_shHDRDolbyMetadataPass1 = nullptr;
}
~CHDRPostProcess()
{
}
void ScreenShot();
void SetShaderParams();
// Tone mapping steps
void HalfResDownsampleHDRTarget();
void QuarterResDownsampleHDRTarget();
void SceneDownsampleUsingCompute(); // for mobile, uses CS to 1/2 & 1/4 res downsample in 1 pass
void MeasureLuminance();
void EyeAdaptation();
void MeasureLumEyeAdaptationUsingCompute(); // uses CS to collapse measure lum and eye adap passes
void BloomGeneration();
void ProcessLensOptics();
void ToneMapping();
void ToneMappingDebug();
void CalculateDolbyDynamicMetadata(CTexture* pSunShaftsRT);
void DrawDebugViews();
void SetExposureTypeShaderFlags();
CCryNameTSCRC GetTonemapTechnique() const;
void EncodeDolbyVision(CTexture* source);
void Begin();
void Render();
void End();
void AddRenderTarget(uint32 nWidth, uint32 nHeight, const ColorF& cClear, ETEX_Format Format, float fPriority, const char* szName, CTexture** pStorage, uint32 nFlags = 0, int32 nCustomID = -1, bool bDynamicTex = 0);
bool CreateRenderTargetList();
void ClearRenderTargetList()
{
m_pRenderTargets.clear();
}
static CHDRPostProcess* GetInstance()
{
static CHDRPostProcess pInstance;
return &pInstance;
}
private:
std::vector< SRenderTargetInfo > m_pRenderTargets;
CShader* m_shHDR;
CShader* m_shHDRDolbyMetadataPass0;
CShader* m_shHDRDolbyMetadataPass1;
WrappedDX11Buffer m_bufDolbyMetadataMacroReductionOutput;
WrappedDX11Buffer m_bufDolbyMetadataMinMaxMid;
int32 nTexStateLinear;
int32 nTexStateLinearWrap;
int32 nTexStatePoint;
int32 nTexStatePointWrap;
bool m_bHiQuality;
};
void CHDRPostProcess::AddRenderTarget(uint32 nWidth, uint32 nHeight, const ColorF& cClear, ETEX_Format Format, float fPriority, const char* szName, CTexture** pStorage, uint32 nFlags, int32 nCustomID, [[maybe_unused]] bool bDynamicTex)
{
SRenderTargetInfo drt;
drt.nWidth = nWidth;
drt.nHeight = nHeight;
drt.cClearColor = cClear;
drt.nFlags = FT_USAGE_RENDERTARGET | FT_DONT_STREAM | nFlags;
drt.Format = Format;
drt.fPriority = fPriority;
drt.lplpStorage = pStorage;
drt.nCustomID = nCustomID;
drt.nPitch = nWidth * CTexture::BytesPerBlock(Format);
cry_strcpy(drt.szName, szName);
m_pRenderTargets.push_back(drt);
}
bool CHDRPostProcess::CreateRenderTargetList()
{
std::sort(m_pRenderTargets.begin(), m_pRenderTargets.end(), RenderTargetSizeSort());
for (uint32 t = 0; t < m_pRenderTargets.size(); ++t)
{
SRenderTargetInfo& drt = m_pRenderTargets[t];
CTexture* pTex = (*drt.lplpStorage);
if (!CTexture::IsTextureExist(pTex))
{
pTex = CTexture::CreateRenderTarget(drt.szName, drt.nWidth, drt.nHeight, drt.cClearColor, eTT_2D, drt.nFlags, drt.Format, drt.nCustomID);
if (pTex)
{
// Following will mess up don't care resolve/restore actions since Fill() sets textures to be cleared on next draw
#if !defined(CRY_USE_METAL) && !defined(OPENGL_ES)
pTex->Clear();
#endif
(*drt.lplpStorage) = pTex;
}
}
else
{
pTex->SetFlags(drt.nFlags);
pTex->SetWidth(drt.nWidth);
pTex->SetHeight(drt.nHeight);
pTex->CreateRenderTarget(drt.Format, drt.cClearColor);
}
}
m_pRenderTargets.clear();
return S_OK;
}
void CTexture::GenerateHDRMaps()
{
int i;
char szName[256];
CD3D9Renderer* r = gcpRendD3D;
CHDRPostProcess* pHDRPostProcess = CHDRPostProcess::GetInstance();
r->m_dwHDRCropWidth = r->GetWidth();
r->m_dwHDRCropHeight = r->GetHeight();
pHDRPostProcess->ClearRenderTargetList();
ETEX_Format nHDRFormat = eTF_R16G16B16A16F; // note: for main rendertarget R11G11B10 precision/range (even with rescaling) not enough for darks vs good blooming quality
ETEX_Format nHDRReducedFormat = r->UseHalfFloatRenderTargets() ? eTF_R11G11B10F : eTF_R10G10B10A2;
uint32 nHDRTargetFlags = FT_DONT_RELEASE | (CRenderer::CV_r_msaa ? FT_USAGE_MSAA : 0);
uint32 nHDRTargetFlagsUAV = nHDRTargetFlags | (CRenderer::CV_r_msaa ? 0 : FT_USAGE_UNORDERED_ACCESS); // UAV required for tiled deferred shading
// GMEM render path uses CTexture::s_ptexSceneSpecularAccMap as the HDR Target
// It gets set in CDeferredShading::CreateDeferredMaps()
if (!gcpRendD3D->FX_GetEnabledGmemPath(nullptr))
{
pHDRPostProcess->AddRenderTarget(r->GetWidth(), r->GetHeight(), Clr_Unknown, r->UseHalfFloatRenderTargets() ? nHDRFormat : nHDRReducedFormat, 1.0f, "$HDRTarget", &s_ptexHDRTarget, nHDRTargetFlagsUAV);
}
pHDRPostProcess->AddRenderTarget(r->GetWidth(), r->GetHeight(), Clr_Unknown, nHDRReducedFormat, 1.0f, "$HDRTargetPrev", &s_ptexHDRTargetPrev);
pHDRPostProcess->AddRenderTarget(r->GetWidth(), r->GetHeight(), Clr_Unknown, nHDRFormat, 1.0f, "$FurLightAcc", &s_ptexFurLightAcc, FT_DONT_RELEASE);
pHDRPostProcess->AddRenderTarget(r->GetWidth(), r->GetHeight(), Clr_Unknown, eTF_R32G32B32A32F, 1.0f, "$FurPrepass", &s_ptexFurPrepass, FT_DONT_RELEASE);
// Scaled versions of the HDR scene texture
uint32 nWeight = (r->m_dwHDRCropWidth >> 1);
uint32 nHeight = (r->m_dwHDRCropHeight >> 1);
pHDRPostProcess->AddRenderTarget(nWeight, nHeight, Clr_Unknown, nHDRFormat, 0.9f, "$HDRTargetScaled0", &s_ptexHDRTargetScaled[0], FT_DONT_RELEASE);
pHDRPostProcess->AddRenderTarget(nWeight, nHeight, Clr_Unknown, nHDRFormat, 0.9f, "$HDRTargetScaledTmp0", &s_ptexHDRTargetScaledTmp[0], FT_DONT_RELEASE);
pHDRPostProcess->AddRenderTarget(nWeight, nHeight, Clr_Unknown, nHDRFormat, 0.9f, "$HDRTargetScaledTempRT0", &s_ptexHDRTargetScaledTempRT[0], FT_DONT_RELEASE);
nWeight = (r->m_dwHDRCropWidth >> 2);
nHeight = (r->m_dwHDRCropHeight >> 2);
pHDRPostProcess->AddRenderTarget(nWeight, nHeight, Clr_Unknown, nHDRFormat, 0.9f, "$HDRTargetScaled1", &s_ptexHDRTargetScaled[1], FT_DONT_RELEASE);
pHDRPostProcess->AddRenderTarget(nWeight, nHeight, Clr_Unknown, nHDRFormat, 0.9f, "$HDRTargetScaledTmp1", &s_ptexHDRTargetScaledTmp[1], FT_DONT_RELEASE);
pHDRPostProcess->AddRenderTarget(nWeight, nHeight, Clr_Unknown, nHDRFormat, 0.9f, "$HDRTargetScaledTempRT1", &s_ptexHDRTargetScaledTempRT[1], 0);
pHDRPostProcess->AddRenderTarget(nWeight, nHeight, Clr_Unknown, eTF_R11G11B10F, 0.9f, "$HDRTempBloom0", &s_ptexHDRTempBloom[0], FT_DONT_RELEASE);
pHDRPostProcess->AddRenderTarget(nWeight, nHeight, Clr_Unknown, eTF_R11G11B10F, 0.9f, "$HDRTempBloom1", &s_ptexHDRTempBloom[1], FT_DONT_RELEASE);
pHDRPostProcess->AddRenderTarget(nWeight, nHeight, Clr_Unknown, eTF_R11G11B10F, 0.9f, "$HDRFinalBloom", &s_ptexHDRFinalBloom, FT_DONT_RELEASE);
pHDRPostProcess->AddRenderTarget((r->m_dwHDRCropWidth >> 3), (r->m_dwHDRCropHeight >> 3), Clr_Unknown, nHDRFormat, 0.9f, "$HDRTargetScaled2", &s_ptexHDRTargetScaled[2], FT_DONT_RELEASE);
pHDRPostProcess->AddRenderTarget((r->m_dwHDRCropWidth >> 3), (r->m_dwHDRCropHeight >> 3), Clr_Unknown, nHDRFormat, 0.9f, "$HDRTargetScaledTempRT2", &s_ptexHDRTargetScaledTempRT[2], FT_DONT_RELEASE);
pHDRPostProcess->AddRenderTarget((r->m_dwHDRCropWidth >> 4), (r->m_dwHDRCropHeight >> 4), Clr_Unknown, nHDRFormat, 0.9f, "$HDRTargetScaled3", &s_ptexHDRTargetScaled[3], FT_DONT_RELEASE);
pHDRPostProcess->AddRenderTarget((r->m_dwHDRCropWidth >> 4), (r->m_dwHDRCropHeight >> 4), Clr_Unknown, nHDRFormat, 0.9f, "$HDRTargetScaledTmp3", &s_ptexHDRTargetScaledTmp[3], FT_DONT_RELEASE);
pHDRPostProcess->AddRenderTarget((r->m_dwHDRCropWidth >> 4), (r->m_dwHDRCropHeight >> 4), Clr_Unknown, nHDRFormat, 0.9f, "$HDRTargetScaledTempRT3", &s_ptexHDRTargetScaledTempRT[3], FT_DONT_RELEASE);
for (i = 0; i < 8; i++)
{
sprintf_s(szName, "$HDRAdaptedLuminanceCur_%d", i);
pHDRPostProcess->AddRenderTarget(1, 1, Clr_Unknown, eTF_R16G16F, 0.1f, szName, &s_ptexHDRAdaptedLuminanceCur[i], FT_DONT_RELEASE);
}
pHDRPostProcess->AddRenderTarget(r->GetWidth(), r->GetHeight(), Clr_Unknown, eTF_R11G11B10F, 1.0f, "$SceneTargetR11G11B10F_0", &s_ptexSceneTargetR11G11B10F[0], nHDRTargetFlagsUAV);
pHDRPostProcess->AddRenderTarget(r->GetWidth(), r->GetHeight(), Clr_Unknown, eTF_R11G11B10F, 1.0f, "$SceneTargetR11G11B10F_1", &s_ptexSceneTargetR11G11B10F[1], nHDRTargetFlags);
pHDRPostProcess->AddRenderTarget(r->m_dwHDRCropWidth, r->m_dwHDRCropHeight, Clr_Unknown, eTF_R8G8B8A8, 0.1f, "$Velocity", &s_ptexVelocity, FT_DONT_RELEASE);
pHDRPostProcess->AddRenderTarget(20, r->m_dwHDRCropHeight, Clr_Unknown, eTF_R8G8B8A8, 0.1f, "$VelocityTilesTmp0", &s_ptexVelocityTiles[0], FT_DONT_RELEASE);
pHDRPostProcess->AddRenderTarget(20, 20, Clr_Unknown, eTF_R8G8B8A8, 0.1f, "$VelocityTilesTmp1", &s_ptexVelocityTiles[1], FT_DONT_RELEASE);
pHDRPostProcess->AddRenderTarget(20, 20, Clr_Transparent, eTF_R8G8B8A8, 0.1f, "$VelocityTiles", &s_ptexVelocityTiles[2], FT_DONT_RELEASE);
ETEX_Format velocityObjectRenderTargetFormat = eTF_R16G16F;
pHDRPostProcess->AddRenderTarget(r->m_dwHDRCropWidth, r->m_dwHDRCropHeight, Clr_Transparent, velocityObjectRenderTargetFormat, 0.1f, "$VelocityObjects", &s_ptexVelocityObjects[0], FT_DONT_RELEASE);
if (gRenDev->m_bDualStereoSupport)
{
pHDRPostProcess->AddRenderTarget(r->m_dwHDRCropWidth, r->m_dwHDRCropHeight, Clr_Unknown, velocityObjectRenderTargetFormat, 0.1f, "$VelocityObject_R", &s_ptexVelocityObjects[1], FT_DONT_RELEASE);
}
pHDRPostProcess->AddRenderTarget(r->GetWidth() >> 1, r->GetHeight() >> 1, Clr_Unknown, nHDRFormat, 0.9f, "$HDRDofLayerNear", &s_ptexHDRDofLayers[0], FT_DONT_RELEASE);
pHDRPostProcess->AddRenderTarget(r->GetWidth() >> 1, r->GetHeight() >> 1, Clr_Unknown, nHDRFormat, 0.9f, "$HDRDofLayerFar", &s_ptexHDRDofLayers[1], FT_DONT_RELEASE);
#if METAL
pHDRPostProcess->AddRenderTarget(r->GetWidth() >> 1, r->GetHeight() >> 1, Clr_Unknown, eTF_R16F, 1.0f, "$MinCoC_0_Temp", &s_ptexSceneCoCTemp, FT_DONT_RELEASE);
#else
pHDRPostProcess->AddRenderTarget(r->GetWidth() >> 1, r->GetHeight() >> 1, Clr_Unknown, eTF_R16G16F, 1.0f, "$MinCoC_0_Temp", &s_ptexSceneCoCTemp, FT_DONT_RELEASE);
#endif
pHDRPostProcess->AddRenderTarget(r->GetWidth(), r->GetHeight(), Clr_Unknown, eTF_R16G16F, 1.0, "$CoC_History0", &s_ptexSceneCoCHistory[0], FT_DONT_RELEASE);
pHDRPostProcess->AddRenderTarget(r->GetWidth(), r->GetHeight(), Clr_Unknown, eTF_R16G16F, 1.0, "$CoC_History1", &s_ptexSceneCoCHistory[1], FT_DONT_RELEASE);
for (i = 0; i < MIN_DOF_COC_K; i++)
{
sprintf_s(szName, "$MinCoC_%d", i);
#if METAL
pHDRPostProcess->AddRenderTarget((r->m_dwHDRCropWidth >> 1) / (i + 1), (r->m_dwHDRCropHeight >> 1) / (i + 1), Clr_Unknown, eTF_R16F, 0.1f, szName, &s_ptexSceneCoC[i], FT_DONT_RELEASE, -1, true);
#else
pHDRPostProcess->AddRenderTarget((r->m_dwHDRCropWidth >> 1) / (i + 1), (r->m_dwHDRCropHeight >> 1) / (i + 1), Clr_Unknown, eTF_R16G16F, 0.1f, szName, &s_ptexSceneCoC[i], FT_DONT_RELEASE, -1, true);
#endif
}
if (gcpRendD3D->FX_GetEnabledGmemPath(nullptr))
{
/* CONFETTI: David Srour
* Used during GMEM path for linear depth & stencil resolve.
* See following link for RT format storage sizes on Apple Metal HW:
* https://developer.apple.com/library/mac/documentation/Miscellaneous/Conceptual/MetalProgrammingGuide/MetalFeatureSetTables/MetalFeatureSetTables.html
*/
ETEX_Format format = eTF_R16G16F;
#if defined(OPENGL_ES) // might be no fp rendering support
if (!gcpRendD3D->UseHalfFloatRenderTargets())
{
format = eTF_R16G16U;
}
#endif
pHDRPostProcess->AddRenderTarget(r->m_dwHDRCropWidth, r->m_dwHDRCropHeight, Clr_Unknown, format, 0.1f,
"$GmemStenLinDepth", &s_ptexGmemStenLinDepth, FT_DONT_RELEASE, -1, true);
}
// Luminance rt
for (i = 0; i < NUM_HDR_TONEMAP_TEXTURES; i++)
{
int iSampleLen = 1 << (2 * i);
sprintf_s(szName, "$HDRToneMap_%d", i);
pHDRPostProcess->AddRenderTarget(iSampleLen, iSampleLen, Clr_Dark, eTF_R16G16F, 0.7f, szName, &s_ptexHDRToneMaps[i], FT_DONT_RELEASE);
}
s_ptexHDRMeasuredLuminanceDummy = CTexture::CreateTextureObject("$HDRMeasuredLum_Dummy", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM, eTF_R16G16F, TO_HDR_MEASURED_LUMINANCE);
for (i = 0; i < MAX_GPU_NUM; ++i)
{
sprintf_s(szName, "$HDRMeasuredLum_%d", i);
if (CRenderer::CV_r_EnableGMEMPostProcCS)
{
pHDRPostProcess->AddRenderTarget(1, 1, Clr_Unknown, eTF_R16G16F, 0.1f, szName, &s_ptexHDRMeasuredLuminance[i], FT_DONT_RELEASE | FT_DONT_STREAM);
}
else
{
s_ptexHDRMeasuredLuminance[i] = CTexture::Create2DTexture(szName, 1, 1, 0, FT_DONT_RELEASE | FT_DONT_STREAM, NULL, eTF_R16G16F, eTF_R16G16F);
}
}
pHDRPostProcess->CreateRenderTargetList();
// Create resources if necessary - todo: refactor all this shared render targets stuff, quite cumbersome atm...
PostProcessUtils().Create();
}
void CTexture::DestroyHDRMaps()
{
int i;
SAFE_RELEASE(s_ptexHDRTarget);
SAFE_RELEASE(s_ptexHDRTargetPrev);
SAFE_RELEASE(s_ptexHDRTargetScaled[0]);
SAFE_RELEASE(s_ptexHDRTargetScaled[1]);
SAFE_RELEASE(s_ptexHDRTargetScaled[2]);
SAFE_RELEASE(s_ptexHDRTargetScaled[3]);
SAFE_RELEASE(s_ptexHDRTargetScaledTmp[0]);
SAFE_RELEASE(s_ptexHDRTargetScaledTempRT[0]);
SAFE_RELEASE(s_ptexHDRTargetScaledTmp[1]);
SAFE_RELEASE(s_ptexHDRTargetScaledTmp[3]);
SAFE_RELEASE(s_ptexHDRTargetScaledTempRT[1]);
SAFE_RELEASE(s_ptexHDRTargetScaledTempRT[2]);
SAFE_RELEASE(s_ptexHDRTargetScaledTempRT[3]);
SAFE_RELEASE(s_ptexHDRTempBloom[0]);
SAFE_RELEASE(s_ptexHDRTempBloom[1]);
SAFE_RELEASE(s_ptexHDRFinalBloom);
for (i = 0; i < 8; i++)
{
SAFE_RELEASE(s_ptexHDRAdaptedLuminanceCur[i]);
}
for (i = 0; i < NUM_HDR_TONEMAP_TEXTURES; i++)
{
SAFE_RELEASE(s_ptexHDRToneMaps[i]);
}
SAFE_RELEASE(s_ptexHDRMeasuredLuminanceDummy);
for (i = 0; i < MAX_GPU_NUM; ++i)
{
SAFE_RELEASE(s_ptexHDRMeasuredLuminance[i]);
}
CTexture::s_ptexCurLumTexture = NULL;
SAFE_RELEASE(s_ptexVelocity);
SAFE_RELEASE(s_ptexVelocityTiles[0]);
SAFE_RELEASE(s_ptexVelocityTiles[1]);
SAFE_RELEASE(s_ptexVelocityTiles[2]);
SAFE_RELEASE(s_ptexVelocityObjects[0]);
SAFE_RELEASE(s_ptexVelocityObjects[1]);
SAFE_RELEASE(s_ptexHDRDofLayers[0]);
SAFE_RELEASE(s_ptexHDRDofLayers[1]);
SAFE_RELEASE(s_ptexSceneCoCTemp);
for (i = 0; i < MIN_DOF_COC_K; i++)
{
SAFE_RELEASE(s_ptexSceneCoC[i]);
}
}
// deprecated
void DrawQuad3D(float s0, float t0, float s1, float t1)
{
const float fZ = 0.5f;
const float fX0 = -1.0f, fX1 = 1.0f;
const float fY0 = 1.0f, fY1 = -1.0f;
gcpRendD3D->DrawQuad3D(Vec3(fX0, fY1, fZ), Vec3(fX1, fY1, fZ), Vec3(fX1, fY0, fZ), Vec3(fX0, fY0, fZ), Col_White, s0, t0, s1, t1);
}
// deprecated
void DrawFullScreenQuadTR(float xpos, float ypos, float w, float h)
{
TempDynVB<SVF_P3F_C4B_T2F> vb(gcpRendD3D);
vb.Allocate(4);
SVF_P3F_C4B_T2F* vQuad = vb.Lock();
const DWORD col = ~0;
const float s0 = 0;
const float s1 = 1;
const float t0 = 1;
const float t1 = 0;
// Define the quad
vQuad[0].xyz = Vec3(xpos, ypos, 1.0f);
vQuad[0].color.dcolor = col;
vQuad[0].st = Vec2(s0, 1.0f - t0);
vQuad[1].xyz = Vec3(xpos + w, ypos, 1.0f);
vQuad[1].color.dcolor = col;
vQuad[1].st = Vec2(s1, 1.0f - t0);
vQuad[3].xyz = Vec3(xpos + w, ypos + h, 1.0f);
vQuad[3].color.dcolor = col;
vQuad[3].st = Vec2(s1, 1.0f - t1);
vQuad[2].xyz = Vec3(xpos, ypos + h, 1.0f);
vQuad[2].color.dcolor = col;
vQuad[2].st = Vec2(s0, 1.0f - t1);
vb.Unlock();
vb.Bind(0);
vb.Release();
gcpRendD3D->FX_Commit();
if (!FAILED(gcpRendD3D->FX_SetVertexDeclaration(0, eVF_P3F_C4B_T2F)))
{
gcpRendD3D->FX_DrawPrimitive(eptTriangleStrip, 0, 4);
}
}
#define NV_CACHE_OPTS_ENABLED TRUE
// deprecated
bool DrawFullScreenQuad(float fLeftU, float fTopV, float fRightU, float fBottomV, bool bClampToScreenRes)
{
CD3D9Renderer* rd = gcpRendD3D;
rd->FX_Commit();
// Acquire render target width and height
int nWidth = rd->m_NewViewport.nWidth;
int nHeight = rd->m_NewViewport.nHeight;
// Ensure that we're directly mapping texels to pixels by offset by 0.5
// For more info see the doc page titled "Directly Mapping Texels to Pixels"
if (bClampToScreenRes)
{
nWidth = min(nWidth, rd->GetWidth());
nHeight = min(nHeight, rd->GetHeight());
}
float fWidth5 = static_cast<float>(nWidth);
float fHeight5 = static_cast<float>(nHeight);
fWidth5 = fWidth5 - 0.5f;
fHeight5 = fHeight5 - 0.5f;
// Draw the quad
TempDynVB<SVF_TP3F_C4B_T2F> vb(gcpRendD3D);
vb.Allocate(4);
SVF_TP3F_C4B_T2F* Verts = vb.Lock();
{
fTopV = 1 - fTopV;
fBottomV = 1 - fBottomV;
Verts[0].pos = Vec4(-0.5f, -0.5f, 0.0f, 1.0f);
Verts[0].color.dcolor = ~0;
Verts[0].st = Vec2(fLeftU, fTopV);
Verts[1].pos = Vec4(fWidth5, -0.5f, 0.0f, 1.0f);
Verts[1].color.dcolor = ~0;
Verts[1].st = Vec2(fRightU, fTopV);
Verts[2].pos = Vec4(-0.5f, fHeight5, 0.0f, 1.0f);
Verts[2].color.dcolor = ~0;
Verts[2].st = Vec2(fLeftU, fBottomV);
Verts[3].pos = Vec4(fWidth5, fHeight5, 0.0f, 1.0f);
Verts[3].color.dcolor = ~0;
Verts[3].st = Vec2(fRightU, fBottomV);
vb.Unlock();
vb.Bind(0);
vb.Release();
rd->FX_SetState(GS_NODEPTHTEST);
if (!FAILED(rd->FX_SetVertexDeclaration(0, eVF_TP3F_C4B_T2F)))
{
rd->FX_DrawPrimitive(eptTriangleStrip, 0, 4);
}
}
return true;
}
// deprecated
bool DrawFullScreenQuad(CoordRect c, bool bClampToScreenRes)
{
return DrawFullScreenQuad(c.fLeftU, c.fTopV, c.fRightU, c.fBottomV, bClampToScreenRes);
}
void GetSampleOffsets_DownScale4x4(uint32 nWidth, uint32 nHeight, Vec4 avSampleOffsets[])
{
float tU = 1.0f / static_cast<float>(nWidth);
float tV = 1.0f / static_cast<float>(nHeight);
// Sample from the 16 surrounding points. Since the center point will be in
// the exact center of 16 texels, a 0.5f offset is needed to specify a texel
// center.
int index = 0;
for (int y = 0; y < 4; y++)
{
for (int x = 0; x < 4; x++)
{
avSampleOffsets[index].x = (x - 1.5f) * tU;
avSampleOffsets[index].y = (y - 1.5f) * tV;
avSampleOffsets[index].z = 0;
avSampleOffsets[index].w = 1;
index++;
}
}
}
void GetSampleOffsets_DownScale4x4Bilinear(uint32 nWidth, uint32 nHeight, Vec4 avSampleOffsets[])
{
float tU = 1.0f / static_cast<float>(nWidth);
float tV = 1.0f / static_cast<float>(nHeight);
// Sample from the 16 surrounding points. Since bilinear filtering is being used, specific the coordinate
// exactly halfway between the current texel center (k-1.5) and the neighboring texel center (k-0.5)
int index = 0;
for (int y = 0; y < 4; y += 2)
{
for (int x = 0; x < 4; x += 2, index++)
{
avSampleOffsets[index].x = (x - 1.f) * tU;
avSampleOffsets[index].y = (y - 1.f) * tV;
avSampleOffsets[index].z = 0;
avSampleOffsets[index].w = 1;
}
}
}
void GetSampleOffsets_DownScale2x2(uint32 nWidth, uint32 nHeight, Vec4 avSampleOffsets[])
{
float tU = 1.0f / static_cast<float>(nWidth);
float tV = 1.0f / static_cast<float>(nHeight);
// Sample from the 4 surrounding points. Since the center point will be in
// the exact center of 4 texels, a 0.5f offset is needed to specify a texel
// center.
int index = 0;
for (int y = 0; y < 2; y++)
{
for (int x = 0; x < 2; x++)
{
avSampleOffsets[index].x = (x - 0.5f) * tU;
avSampleOffsets[index].y = (y - 0.5f) * tV;
avSampleOffsets[index].z = 0;
avSampleOffsets[index].w = 1;
index++;
}
}
}
void CHDRPostProcess::SetShaderParams()
{
CD3D9Renderer* r = gcpRendD3D;
Vec4 vHDRSetupParams[5];
gEnv->p3DEngine->GetHDRSetupParams(vHDRSetupParams);
static CCryNameR szHDREyeAdaptationParam("HDREyeAdaptation");
m_shHDR->FXSetPSFloat(szHDREyeAdaptationParam, CRenderer::CV_r_HDREyeAdaptationMode == 2 ? &vHDRSetupParams[4] : &vHDRSetupParams[3], 1);
// RGB Film curve setup
static CCryNameR szHDRFilmCurve("HDRFilmCurve");
const Vec4 vHDRFilmCurve = vHDRSetupParams[0]; //* Vec4(0.22f, 0.3f, 0.01f, 1.0f);
m_shHDR->FXSetPSFloat(szHDRFilmCurve, &vHDRFilmCurve, 1);
static CCryNameR szHDRColorBalance("HDRColorBalance");
const Vec4 vHDRColorBalance = vHDRSetupParams[2];
m_shHDR->FXSetPSFloat(szHDRColorBalance, &vHDRColorBalance, 1);
static CCryNameR szHDRBloomColor("HDRBloomColor");
const Vec4 vHDRBloomColor = vHDRSetupParams[1] * Vec4(Vec3((1.0f / 8.0f)), 1.0f); // division by 8.0f was done in shader before, remove this at some point
m_shHDR->FXSetPSFloat(szHDRBloomColor, &vHDRBloomColor, 1);
if (CRenderer::CV_r_ToneMapExposureType == static_cast<int>(Exposuretype::Manual))
{
static CCryNameR tonemapParams("HDRTonemapParams");
Vec4 v = Vec4(CRenderer::CV_r_ToneMapManualExposureValue, 0, 0, 0);
m_shHDR->FXSetPSFloat(tonemapParams, &v, 1);
}
}
void CHDRPostProcess::SceneDownsampleUsingCompute()
{
CTexture* pSrcRT = CTexture::s_ptexHDRTarget;
CTexture* pDstRTs[3] =
{
CTexture::s_ptexHDRTargetScaled[0],
CTexture::s_ptexHDRTargetScaled[1],
nullptr
};
PostProcessUtils().DownsampleUsingCompute(pSrcRT, pDstRTs);
}
void CHDRPostProcess::HalfResDownsampleHDRTarget()
{
PROFILE_LABEL_SCOPE("HALFRES_DOWNSAMPLE_HDRTARGET");
CTexture* pSrcRT = CTexture::s_ptexHDRTarget;
CTexture* pDstRT = CTexture::s_ptexHDRTargetScaled[0];
#if defined(CRY_USE_METAL) || defined(ANDROID)
gRenDev->RT_SetScissor(true, 0, 0, gcpRendD3D->m_HalfResRect.right, gcpRendD3D->m_HalfResRect.bottom);
#endif
if (CRenderer::CV_r_HDRBloomQuality >= 2)
{
PostProcessUtils().DownsampleStable(pSrcRT, pDstRT, true);
}
else
{
PostProcessUtils().StretchRect(pSrcRT, pDstRT, true);
}
#ifdef CRY_USE_METAL
gRenDev->RT_SetScissor(false, 0, 0, 0, 0);
#endif
}
void CHDRPostProcess::QuarterResDownsampleHDRTarget()
{
PROFILE_LABEL_SCOPE("QUARTER_RES_DOWNSAMPLE_HDRTARGET");
CTexture* pSrcRT = CTexture::s_ptexHDRTargetScaled[0];
CTexture* pDstRT = CTexture::s_ptexHDRTargetScaled[1];
#if defined(CRY_USE_METAL) || defined(ANDROID)
gRenDev->RT_SetScissor(true, 0, 0, (gcpRendD3D->m_HalfResRect.right + 1) >> 1, (gcpRendD3D->m_HalfResRect.bottom + 1) >> 1);
#endif
// TODO: this pass seems redundant. Can we get rid of it in non-gmem paths too?
if (!gcpRendD3D->FX_GetEnabledGmemPath(nullptr))
{
PostProcessUtils().DownsampleStable(pSrcRT, pDstRT, false);
}
// Try to merge both sunshafts mask gen with the scene downsample on GMEM mobile path
CSunShafts* pSunShaftsTech = static_cast<CSunShafts*>(PostEffectMgr()->GetEffect(ePFX_SunShafts));
if (gcpRendD3D->FX_GetEnabledGmemPath(nullptr) &&
CRenderer::CV_r_sunshafts &&
CRenderer::CV_r_PostProcess &&
pSunShaftsTech &&
pSunShaftsTech->IsVisible())
{
// It is important that the following texture remains untouched until the sunshafts passes right
// before tonemapping. At the moment, it doesn't look like any other passes make use of the RT.
// This RT also must match what is passed later on to CSunShafts::SunShaftsGen(...)
CTexture* pSunShaftsRT = CTexture::s_ptexBackBufferScaled[1];
pSunShaftsTech->MergedSceneDownsampleAndSunShaftsMaskGen(pSrcRT, pDstRT, pSunShaftsRT);
}
else if (CRenderer::CV_r_HDRBloomQuality >= 2)
{
PostProcessUtils().DownsampleStable(pSrcRT, pDstRT, false);
}
else if (CRenderer::CV_r_HDRBloomQuality == 1)
{
PostProcessUtils().DownsampleStable(pSrcRT, pDstRT, true);
}
else
{
PostProcessUtils().StretchRect(pSrcRT, pDstRT);
}
#if defined(CRY_USE_METAL) || defined(ANDROID)
gRenDev->RT_SetScissor(false, 0, 0, 0, 0);
#endif
}
void CHDRPostProcess::MeasureLuminance()
{
PROFILE_LABEL_SCOPE("MEASURE_LUMINANCE");
int x, y, index;
Vec4 avSampleOffsets[16];
CD3D9Renderer* rd = gcpRendD3D;
uint64 nFlagsShader_RT = gRenDev->m_RP.m_FlagsShader_RT;
gRenDev->m_RP.m_FlagsShader_RT &= ~(g_HWSR_MaskBit[HWSR_SAMPLE0] | g_HWSR_MaskBit[HWSR_SAMPLE1] | g_HWSR_MaskBit[HWSR_SAMPLE2] | g_HWSR_MaskBit[HWSR_SAMPLE4] | g_HWSR_MaskBit[HWSR_SAMPLE5]);
if (CRenderer::CV_r_SlimGBuffer == 1)
{
gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SLIM_GBUFFER];
}
int32 dwCurTexture = NUM_HDR_TONEMAP_TEXTURES - 1;
static CCryNameR Param1Name("SampleOffsets");
float tU = 1.0f / (3.0f * CTexture::s_ptexHDRToneMaps[dwCurTexture]->GetWidth());
float tV = 1.0f / (3.0f * CTexture::s_ptexHDRToneMaps[dwCurTexture]->GetHeight());
index = 0;
for (x = -1; x <= 1; x++)
{
for (y = -1; y <= 1; y++)
{
avSampleOffsets[index].x = x * tU;
avSampleOffsets[index].y = y * tV;
avSampleOffsets[index].z = 0;
avSampleOffsets[index].w = 1;
index++;
}
}
uint32 nPasses;
rd->FX_PushRenderTarget(0, CTexture::s_ptexHDRToneMaps[dwCurTexture], NULL);
rd->FX_SetColorDontCareActions(0, true, false);
rd->FX_SetDepthDontCareActions(0, true, true);
rd->FX_SetStencilDontCareActions(0, true, true);
rd->FX_SetActiveRenderTargets();
rd->RT_SetViewport(0, 0, CTexture::s_ptexHDRToneMaps[dwCurTexture]->GetWidth(), CTexture::s_ptexHDRToneMaps[dwCurTexture]->GetHeight());
if (CRenderer::CV_r_HDREyeAdaptationMode == 2)
{
gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE4];
}
else
{
CTexture::s_ptexSceneNormalsMap->Apply(1, nTexStateLinear);
CTexture::s_ptexSceneDiffuse->Apply(2, nTexStateLinear);
CTexture::s_ptexSceneSpecular->Apply(3, nTexStateLinear);
}
static CCryNameTSCRC TechName("HDRSampleLumInitial");
m_shHDR->FXSetTechnique(TechName);
m_shHDR->FXBegin(&nPasses, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
m_shHDR->FXBeginPass(0);
CTexture::s_ptexHDRTargetScaled[1]->Apply(0, nTexStateLinear);
float s1 = 1.0f / static_cast<float>(CTexture::s_ptexHDRTargetScaled[1]->GetWidth());
float t1 = 1.0f / static_cast<float>(CTexture::s_ptexHDRTargetScaled[1]->GetHeight());
// Use rotated grid
Vec4 vSampleLumOffsets0 = Vec4(s1 * 0.95f, t1 * 0.25f, -s1 * 0.25f, t1 * 0.96f);
Vec4 vSampleLumOffsets1 = Vec4(-s1 * 0.96f, -t1 * 0.25f, s1 * 0.25f, -t1 * 0.96f);
static CCryNameR pSampleLumOffsetsName0("SampleLumOffsets0");
static CCryNameR pSampleLumOffsetsName1("SampleLumOffsets1");
m_shHDR->FXSetPSFloat(pSampleLumOffsetsName0, &vSampleLumOffsets0, 1);
m_shHDR->FXSetPSFloat(pSampleLumOffsetsName1, &vSampleLumOffsets1, 1);
SetShaderParams();
bool ret = DrawFullScreenQuad(0.0f, 1.0f - 1.0f * gcpRendD3D->m_CurViewportScale.y, 1.0f * gcpRendD3D->m_CurViewportScale.x, 1.0f);
// important that we always write out valid luminance, even if quad draw fails
if (!ret)
{
rd->FX_ClearTarget(CTexture::s_ptexHDRToneMaps[dwCurTexture], Clr_Dark);
}
m_shHDR->FXEndPass();
rd->FX_PopRenderTarget(0);
dwCurTexture--;
// Initialize the sample offsets for the iterative luminance passes
while (dwCurTexture >= 0)
{
rd->FX_PushRenderTarget(0, CTexture::s_ptexHDRToneMaps[dwCurTexture], NULL);
// CONFETTI BEGIN: David Srour
// Metal Load/Store Actions
rd->FX_SetColorDontCareActions(0, true, false);
rd->FX_SetDepthDontCareActions(0, true, true);
rd->FX_SetStencilDontCareActions(0, true, true);
// CONFETTI END
rd->RT_SetViewport(0, 0, CTexture::s_ptexHDRToneMaps[dwCurTexture]->GetWidth(), CTexture::s_ptexHDRToneMaps[dwCurTexture]->GetHeight());
if (!dwCurTexture)
{
gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE0];
}
if (dwCurTexture == 1)
{
gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE1];
}
static CCryNameTSCRC TechNameLI("HDRSampleLumIterative");
m_shHDR->FXSetTechnique(TechNameLI);
m_shHDR->FXBegin(&nPasses, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
m_shHDR->FXBeginPass(0);
GetSampleOffsets_DownScale4x4Bilinear(CTexture::s_ptexHDRToneMaps[dwCurTexture + 1]->GetWidth(), CTexture::s_ptexHDRToneMaps[dwCurTexture + 1]->GetHeight(), avSampleOffsets);
m_shHDR->FXSetPSFloat(Param1Name, avSampleOffsets, 4);
CTexture::s_ptexHDRToneMaps[dwCurTexture + 1]->Apply(0, nTexStateLinear);
// Draw a fullscreen quad to sample the RT
ret = DrawFullScreenQuad(0.0f, 0.0f, 1.0f, 1.0f);
// important that we always write out valid luminance, even if quad draw fails
if (!ret)
{
rd->FX_ClearTarget(CTexture::s_ptexHDRToneMaps[dwCurTexture], Clr_Dark);
}
m_shHDR->FXEndPass();
rd->FX_PopRenderTarget(0);
dwCurTexture--;
}
gcpRendD3D->GetDeviceContext().CopyResource(
CTexture::s_ptexHDRMeasuredLuminance[gcpRendD3D->RT_GetCurrGpuID()]->GetDevTexture()->GetBaseTexture(),
CTexture::s_ptexHDRToneMaps[0]->GetDevTexture()->GetBaseTexture());
gRenDev->m_RP.m_FlagsShader_RT = nFlagsShader_RT;
}
void CHDRPostProcess::EyeAdaptation()
{
PROFILE_LABEL_SCOPE("EYEADAPTATION");
CD3D9Renderer* rd = gcpRendD3D;
// Swap current & last luminance
const int32 lumMask = static_cast<int32>((sizeof(CTexture::s_ptexHDRAdaptedLuminanceCur) / sizeof(CTexture::s_ptexHDRAdaptedLuminanceCur[0]))) - 1;
const int32 numTextures = static_cast<int32>(max(min(gRenDev->GetActiveGPUCount(), static_cast<uint32>(sizeof(CTexture::s_ptexHDRAdaptedLuminanceCur) / sizeof(CTexture::s_ptexHDRAdaptedLuminanceCur[0]))), 1u));
#if AZ_RENDER_TO_TEXTURE_GEM_ENABLED
// only increment the current luminance texture when drawing the main scene
if (!(rd->m_RP.m_TI[rd->m_RP.m_nProcessThreadID].m_PersFlags & RBPF_RENDER_SCENE_TO_TEXTURE))
{
CTexture::s_nCurLumTextureIndex++;
}
#else
CTexture::s_nCurLumTextureIndex++;
#endif // if AZ_RENDER_TO_TEXTURE_GEM_ENABLED
CTexture* pTexPrev = CTexture::s_ptexHDRAdaptedLuminanceCur[(CTexture::s_nCurLumTextureIndex - numTextures) & lumMask];
CTexture* pTexCur = CTexture::s_ptexHDRAdaptedLuminanceCur[CTexture::s_nCurLumTextureIndex & lumMask];
CTexture::s_ptexCurLumTexture = pTexCur;
CRY_ASSERT(pTexCur);
uint32 nPasses;
static CCryNameTSCRC TechName("HDRCalculateAdaptedLum");
m_shHDR->FXSetTechnique(TechName);
m_shHDR->FXBegin(&nPasses, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
rd->FX_PushRenderTarget(0, pTexCur, NULL);
// CONFETTI BEGIN: David Srour
// Metal Load/Store Actions
rd->FX_SetColorDontCareActions(0, true, false);
rd->FX_SetDepthDontCareActions(0, true, true);
rd->FX_SetStencilDontCareActions(0, true, true);
// CONFETTI END
rd->RT_SetViewport(0, 0, pTexCur->GetWidth(), pTexCur->GetHeight());
m_shHDR->FXBeginPass(0);
SetShaderParams();
static CCryNameR Param1Name("ElapsedTime");
{
Vec4 elapsedTime;
elapsedTime[0] = iTimer->GetFrameTime() * numTextures;
elapsedTime[1] = 1.0f - expf(-CRenderer::CV_r_HDREyeAdaptationSpeed * elapsedTime[0]);
elapsedTime[2] = 0;
elapsedTime[3] = 0;
if (rd->GetCamera().IsJustActivated() || rd->m_nDisableTemporalEffects > 0)
{
elapsedTime[1] = 1.0f;
elapsedTime[2] = 1.0f;
}
m_shHDR->FXSetPSFloat(Param1Name, &elapsedTime, 1);
}
pTexPrev->Apply(0, nTexStatePoint);
CTexture::s_ptexHDRToneMaps[0]->Apply(1, nTexStatePoint);
// Draw a fullscreen quad to sample the RT
DrawFullScreenQuad(0.0f, 0.0f, 1.0f, 1.0f);
m_shHDR->FXEndPass();
rd->FX_PopRenderTarget(0);
}
void CHDRPostProcess::MeasureLumEyeAdaptationUsingCompute()
{
PROFILE_LABEL_SCOPE("MEASURE_LUM_EYE_ADAPT_CS");
PROFILE_SHADER_SCOPE;
CD3D9Renderer* const __restrict rd = gcpRendD3D;
// Constants used by CS shaders
float hdrTargetWidth = static_cast<float>(CTexture::s_ptexHDRTargetScaled[1]->GetWidth());
float hdrTargetHeight = static_cast<float>(CTexture::s_ptexHDRTargetScaled[1]->GetHeight());
int lumStartingWidth = CTexture::s_ptexHDRToneMaps[NUM_HDR_TONEMAP_TEXTURES - 1]->GetWidth();
int lumStartingHeight = CTexture::s_ptexHDRToneMaps[NUM_HDR_TONEMAP_TEXTURES - 1]->GetHeight();
Vec4 vHdrTargetLumStartDims = Vec4(hdrTargetWidth, hdrTargetHeight, static_cast<float>(lumStartingWidth), static_cast<float>(lumStartingHeight));
Vec4 vGBufferDims = Vec4(static_cast<float>(CTexture::s_ptexSceneDiffuse->GetWidth()), static_cast<float>(CTexture::s_ptexSceneDiffuse->GetHeight()), 0, 0);
const int lumMask = static_cast<int32>((sizeof(CTexture::s_ptexHDRAdaptedLuminanceCur) / sizeof(CTexture::s_ptexHDRAdaptedLuminanceCur[0]))) - 1;
const int32 numTextures = static_cast<int32>(max(min(gRenDev->GetActiveGPUCount(), static_cast<uint32>(sizeof(CTexture::s_ptexHDRAdaptedLuminanceCur) / sizeof(CTexture::s_ptexHDRAdaptedLuminanceCur[0]))), 1u));
static CCryNameR pParamTimeName("ElapsedTime");
static CCryNameR pHdrTargetLumStartDimsName("HdrTargetAndLumStartingDims");
static CCryNameR pGbufferDimsName("GBufferDims");
static CCryNameTSCRC pTech("MeasureLuminanceCS");
uint32 nPasses;
m_shHDR->FXSetTechnique(pTech);
m_shHDR->FXBegin(&nPasses, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
D3DShaderResourceView* pSRV[3];
D3DUnorderedAccessView* pUAV[3];
// Grid dims must match in shader
const uint32 kernelGridX = 8;
const uint32 kernelGridY = 8;
uint32 dispatchSizeX, dispatchSizeY;
// Parallel reduction pass
m_shHDR->FXBeginPass(0);
m_shHDR->FXSetCSFloat(pHdrTargetLumStartDimsName, &vHdrTargetLumStartDims, 1);
m_shHDR->FXSetCSFloat(pGbufferDimsName, &vGBufferDims, 1);
rd->FX_Commit();
// SRVs
pSRV[0] = (CTexture::s_ptexHDRTargetScaled[1]->GetShaderResourceView());
pSRV[1] = (CTexture::s_ptexSceneDiffuse->GetShaderResourceView());
pSRV[2] = (CTexture::s_ptexSceneSpecular->GetShaderResourceView());
rd->m_DevMan.BindSRV(eHWSC_Compute, pSRV, 0, 3);
// UAVs
// We can reuse CTexture::s_ptexHDRToneMaps[2] (16x16) to store the last parallel reduction data (64 pixels required)
// Note that this will potentially need to be changed if NUM_HDR_TONEMAP_TEXTURES changes in the future.
pUAV[0] = (CTexture::s_ptexHDRToneMaps[2]->GetDeviceUAV());
rd->GetDeviceContext().CSSetUnorderedAccessViews(0, 1, pUAV, NULL);
dispatchSizeX = lumStartingWidth / kernelGridX + (lumStartingWidth % kernelGridX > 0 ? 1 : 0);
dispatchSizeY = lumStartingHeight / kernelGridY + (lumStartingHeight % kernelGridY > 0 ? 1 : 0);
rd->m_DevMan.Dispatch(dispatchSizeX, dispatchSizeY, 1);
m_shHDR->FXEndPass();
// Final reduction and eye adaptation pass
m_shHDR->FXBeginPass(1);
m_shHDR->FXSetCSFloat(pHdrTargetLumStartDimsName, &vHdrTargetLumStartDims, 1);
{
Vec4 elapsedTime;
elapsedTime[0] = iTimer->GetFrameTime() * numTextures;
elapsedTime[1] = 1.0f - expf(-CRenderer::CV_r_HDREyeAdaptationSpeed * elapsedTime[0]);
elapsedTime[2] = 0;
elapsedTime[3] = 0;
if (rd->GetCamera().IsJustActivated() || rd->m_nDisableTemporalEffects > 0)
{
elapsedTime[1] = 1.0f;
elapsedTime[2] = 1.0f;
}
m_shHDR->FXSetCSFloat(pParamTimeName, &elapsedTime, 1);
}
// Swap current & last luminance
CTexture::s_nCurLumTextureIndex++;
CTexture* pTexPrev = CTexture::s_ptexHDRAdaptedLuminanceCur[(CTexture::s_nCurLumTextureIndex - numTextures) & lumMask];
CTexture* pTexCur = CTexture::s_ptexHDRAdaptedLuminanceCur[CTexture::s_nCurLumTextureIndex & lumMask];
CTexture::s_ptexCurLumTexture = pTexCur;
CRY_ASSERT(pTexCur);
// SRVs
pSRV[0] = (CTexture::s_ptexHDRToneMaps[2]->GetShaderResourceView());
pSRV[1] = (pTexPrev->GetShaderResourceView());
rd->m_DevMan.BindSRV(eHWSC_Compute, pSRV, 0, 2);
// UAVs
pUAV[0] = (CTexture::s_ptexHDRMeasuredLuminance[gcpRendD3D->RT_GetCurrGpuID()]->GetDeviceUAV());
pUAV[1] = (CTexture::s_ptexHDRToneMaps[0]->GetDeviceUAV());
pUAV[2] = (pTexCur->GetDeviceUAV());
rd->GetDeviceContext().CSSetUnorderedAccessViews(0, 3, pUAV, NULL);
dispatchSizeX = lumStartingWidth / (kernelGridX * kernelGridY);
dispatchSizeY = lumStartingHeight / (kernelGridX * kernelGridY);
CRY_ASSERT(1 == dispatchSizeX && 1 == dispatchSizeY);
rd->m_DevMan.Dispatch(dispatchSizeX, dispatchSizeY, 1);
m_shHDR->FXEndPass();
rd->FX_Commit();
}
void CHDRPostProcess::BloomGeneration()
{
if (CRenderer::CV_r_GraphicsPipeline & 1)
{
gcpRendD3D->GetGraphicsPipeline().RenderBloom();
return;
}
// Approximate function (1 - r)^4 by a sum of Gaussians: 0.0174*G(0.008,r) + 0.192*G(0.0576,r)
const float sigma1 = sqrtf(0.008f);
const float sigma2 = sqrtf(0.0576f - 0.008f);
PROFILE_LABEL_SCOPE("BLOOM_GEN");
CD3D9Renderer* rd = gcpRendD3D;
static CCryNameTSCRC TechName("HDRBloomGaussian");
static CCryNameR szHDRParam0("HDRParams0");
uint64 nPrevFlagsShaderRT = rd->m_RP.m_FlagsShader_RT;
rd->m_RP.m_FlagsShader_RT &= ~(g_HWSR_MaskBit[HWSR_SAMPLE0]);
int width = CTexture::s_ptexHDRFinalBloom->GetWidth();
int height = CTexture::s_ptexHDRFinalBloom->GetHeight();
// Note: Just scaling the sampling offsets depending on the resolution is not very accurate but works acceptably
CRY_ASSERT(CTexture::s_ptexHDRFinalBloom->GetWidth() == CTexture::s_ptexHDRTarget->GetWidth() / 4);
float scaleW = (static_cast<float>(width) / 400.0f) / static_cast<float>(width);
float scaleH = (static_cast<float>(height) / 225.0f) / static_cast<float>(height);
int32 texFilter = (CTexture::s_ptexHDRFinalBloom->GetWidth() == 400 && CTexture::s_ptexHDRFinalBloom->GetHeight() == 225) ? nTexStatePoint : nTexStateLinear;
rd->FX_SetState(GS_NODEPTHTEST);
rd->RT_SetViewport(0, 0, width, height);
// Pass 1 Horizontal
rd->FX_PushRenderTarget(0, CTexture::s_ptexHDRTempBloom[1], NULL);
rd->FX_SetColorDontCareActions(0, true, false);
rd->FX_SetDepthDontCareActions(0, true, true);
rd->FX_SetStencilDontCareActions(0, true, true);
SD3DPostEffectsUtils::ShBeginPass(m_shHDR, TechName, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
Vec4 v = Vec4(scaleW, 0, 0, 0);
m_shHDR->FXSetPSFloat(szHDRParam0, &v, 1);
CTexture::s_ptexHDRTargetScaled[1]->Apply(0, texFilter);
SPostEffectsUtils::DrawFullScreenTri(width, height);
SD3DPostEffectsUtils::ShEndPass();
rd->FX_PopRenderTarget(0);
// Pass 1 Vertical
if (gcpRendD3D->FX_GetEnabledGmemPath(nullptr))
{
rd->FX_PushRenderTarget(0, CTexture::s_ptexHDRFinalBloom, NULL);
}
else
{
rd->FX_PushRenderTarget(0, CTexture::s_ptexHDRTempBloom[0], NULL);
}
rd->FX_SetColorDontCareActions(0, true, false);
rd->FX_SetDepthDontCareActions(0, true, true);
rd->FX_SetStencilDontCareActions(0, true, true);
SD3DPostEffectsUtils::ShBeginPass(m_shHDR, TechName, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
v = Vec4(0, scaleH, 0, 0);
m_shHDR->FXSetPSFloat(szHDRParam0, &v, 1);
CTexture::s_ptexHDRTempBloom[1]->Apply(0, texFilter);
SPostEffectsUtils::DrawFullScreenTri(width, height);
SD3DPostEffectsUtils::ShEndPass();
rd->FX_PopRenderTarget(0);
// For mobile we skip the second blur pass for performance reasons
if (!gcpRendD3D->FX_GetEnabledGmemPath(nullptr))
{
// Pass 2 Horizontal
rd->FX_PushRenderTarget(0, CTexture::s_ptexHDRTempBloom[1], NULL);
rd->FX_SetColorDontCareActions(0, true, false);
rd->FX_SetDepthDontCareActions(0, true, true);
rd->FX_SetStencilDontCareActions(0, true, true);
SD3DPostEffectsUtils::ShBeginPass(m_shHDR, TechName, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
v = Vec4((sigma2 / sigma1) * scaleW, 0, 0, 0);
m_shHDR->FXSetPSFloat(szHDRParam0, &v, 1);
CTexture::s_ptexHDRTempBloom[0]->Apply(0, texFilter);
SPostEffectsUtils::DrawFullScreenTri(width, height);
SD3DPostEffectsUtils::ShEndPass();
rd->FX_PopRenderTarget(0);
// Pass 2 Vertical
rd->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE0];
rd->FX_PushRenderTarget(0, CTexture::s_ptexHDRFinalBloom, NULL);
rd->FX_SetColorDontCareActions(0, true, false);
rd->FX_SetDepthDontCareActions(0, true, true);
rd->FX_SetStencilDontCareActions(0, true, true);
SD3DPostEffectsUtils::ShBeginPass(m_shHDR, TechName, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
v = Vec4(0, (sigma2 / sigma1) * scaleH, 0, 0);
m_shHDR->FXSetPSFloat(szHDRParam0, &v, 1);
CTexture::s_ptexHDRTempBloom[1]->Apply(0, texFilter);
CTexture::s_ptexHDRTempBloom[0]->Apply(1, texFilter);
SPostEffectsUtils::DrawFullScreenTri(width, height);
SD3DPostEffectsUtils::ShEndPass();
rd->FX_PopRenderTarget(0);
}
rd->m_RP.m_FlagsShader_RT = nPrevFlagsShaderRT;
}
void CHDRPostProcess::ProcessLensOptics()
{
gcpRendD3D->m_RP.m_PersFlags2 &= ~RBPF2_LENS_OPTICS_COMPOSITE;
if (CRenderer::CV_r_flares && CRenderer::CV_r_PostProcess)
{
const uint32 nBatchMask = SRendItem::BatchFlags(EFSLIST_LENSOPTICS, gcpRendD3D->m_RP.m_pRLD);
if (nBatchMask & (FB_GENERAL | FB_TRANSPARENT))
{
PROFILE_LABEL_SCOPE("LENS_OPTICS");
CTexture* pLensOpticsComposite = CTexture::s_ptexBackBufferScaledTemp[0];
pLensOpticsComposite = CTexture::s_ptexSceneTargetR11G11B10F[0];
gcpRendD3D->FX_PushRenderTarget(0, pLensOpticsComposite, 0);
gcpRendD3D->FX_SetColorDontCareActions(0, false, false);
gcpRendD3D->FX_ClearTarget(pLensOpticsComposite, Clr_Transparent);
gcpRendD3D->m_RP.m_PersFlags2 |= RBPF2_NOPOSTAA;
GetUtils().Log(" +++ Begin lens-optics scene +++ \n");
gcpRendD3D->FX_ProcessRenderList(EFSLIST_LENSOPTICS, FB_GENERAL);
gcpRendD3D->FX_ProcessRenderList(EFSLIST_LENSOPTICS, FB_TRANSPARENT);
gcpRendD3D->FX_ResetPipe();
GetUtils().Log(" +++ End lens-optics scene +++ \n");
gcpRendD3D->FX_SetActiveRenderTargets();
gcpRendD3D->FX_PopRenderTarget(0);
gcpRendD3D->FX_SetActiveRenderTargets();
}
}
}
enum class eHDRPostProcessSRVs : int
{
HDRInput = 0,
Luminance = 1,
Bloom = 2,
Velocity = 3,
ZTarget = 5,
VignetteMap = 7,
ColorChar = 8,
SunShafts = 9,
DolbyVisionDynamicMeta = 15
};
void CHDRPostProcess::ToneMapping()
{
CD3D9Renderer* rd = gcpRendD3D;
SRenderPipeline& RESTRICT_REFERENCE rRP = rd->m_RP;
if (!gcpRendD3D->m_pColorGradingControllerD3D)
{
return;
}
CSunShafts* pSunShaftsTech = static_cast<CSunShafts*>(PostEffectMgr()->GetEffect(ePFX_SunShafts));
CTexture* pSunShaftsRT = CTextureManager::Instance()->GetBlackTexture();
if (CRenderer::CV_r_sunshafts && CRenderer::CV_r_PostProcess && pSunShaftsTech && pSunShaftsTech->IsVisible())
{
// Create shafts mask texture
uint32 nWidth = CTexture::s_ptexBackBufferScaled[1]->GetWidth();
uint32 nHeight = CTexture::s_ptexBackBufferScaled[1]->GetHeight();
pSunShaftsRT = CTexture::s_ptexBackBufferScaled[1];
CTexture* pSunShaftsPingPongRT = CTexture::s_ptexBackBufferScaledTemp[1];
if (rRP.m_eQuality >= eRQ_High &&
!gcpRendD3D->FX_GetEnabledGmemPath(nullptr)) // GMEM always uses the downsampled target
{
pSunShaftsRT = CTexture::s_ptexBackBufferScaled[0];
pSunShaftsPingPongRT = CTexture::s_ptexBackBufferScaledTemp[0];
nWidth = CTexture::s_ptexBackBufferScaled[0]->GetWidth();
nHeight = CTexture::s_ptexBackBufferScaled[0]->GetHeight();
}
pSunShaftsTech->SunShaftsGen(pSunShaftsRT, pSunShaftsPingPongRT);
}
// Update color grading
bool bColorGrading = false;
SColorGradingMergeParams pMergeParams;
if (CRenderer::CV_r_colorgrading && CRenderer::CV_r_colorgrading_charts)
{
CColorGrading* pColorGrad = 0;
if (!PostEffectMgr()->GetEffects().empty())
{
pColorGrad = static_cast<CColorGrading*>(PostEffectMgr()->GetEffect(ePFX_ColorGrading));
}
if (pColorGrad && pColorGrad->UpdateParams(pMergeParams))
{
bColorGrading = true;
}
}
CColorGradingControllerD3D* pCtrl = gcpRendD3D->m_pColorGradingControllerD3D;
CTexture* pTexColorChar = pCtrl ? pCtrl->GetColorChart() : 0;
rd->RT_SetViewport(0, 0, CTexture::s_ptexHDRTarget->GetWidth(), CTexture::s_ptexHDRTarget->GetHeight());
PROFILE_LABEL_SCOPE("TONEMAPPING");
// Enable corresponding shader variation
rRP.m_FlagsShader_RT &= ~(g_HWSR_MaskBit[HWSR_SAMPLE0] | g_HWSR_MaskBit[HWSR_SAMPLE1] | g_HWSR_MaskBit[HWSR_SAMPLE2] | g_HWSR_MaskBit[HWSR_SAMPLE3] | g_HWSR_MaskBit[HWSR_SAMPLE4] | g_HWSR_MaskBit[HWSR_SAMPLE5]);
if (CRenderer::CV_r_HDREyeAdaptationMode == 2)
{
rRP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE4];
}
SetExposureTypeShaderFlags();
if (bColorGrading && pTexColorChar)
{
rRP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE1];
}
if (CRenderer::CV_r_HDRDebug == 5)
{
rRP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_DEBUG0];
}
const uint32 nAAMode = rd->FX_GetAntialiasingType();
if (nAAMode & eAT_FXAA_MASK)
{
rRP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE0];
}
#if AZ_RENDER_TO_TEXTURE_GEM_ENABLED
// Disable gamma application if requested
if (!CRenderer::CV_r_FinalOutputsRGB)
{
rRP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE2];
}
// Output fully opaque alpha if rendering to texture without AA or depth
if ((nAAMode & eAT_NOAA_MASK) && rd->IsRenderToTextureActive())
{
rRP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE3];
}
#endif // if AZ_RENDER_TO_TEXTURE_GEM_ENABLED
PostProcessUtils().SetSRGBShaderFlags();
rd->FX_SetColorDontCareActions(0, true, false);
rd->FX_SetStencilDontCareActions(0, true, true);
rd->FX_SetColorDontCareActions(1, true, false);
rd->FX_SetStencilDontCareActions(1, true, true);
bool isAfterPostProcessBucketEmpty = SRendItem::IsListEmpty(EFSLIST_AFTER_POSTPROCESS, rd->m_RP.m_nProcessThreadID, rd->m_RP.m_pRLD);
bool isAuxGeomEnabled = false;
#if defined(ENABLE_RENDER_AUX_GEOM)
isAuxGeomEnabled = CRenderer::CV_r_enableauxgeom == 1;
#endif
//We may need to preserve the depth buffer in case there is something to render in the EFSLIST_AFTER_POSTPROCESS bucket.
//It could be UI in the 3d world. If the bucket is empty ignore the depth buffer as it is not needed.
//Also check if Auxgeom rendering is enabled in which case we preserve depth buffer.
if (isAfterPostProcessBucketEmpty && !isAuxGeomEnabled)
{
rd->FX_SetDepthDontCareActions(0, true, true);
rd->FX_SetDepthDontCareActions(1, true, true);
}
else
{
rd->FX_SetDepthDontCareActions(0, false, false);
rd->FX_SetDepthDontCareActions(1, false, false);
}
// Final bloom RT
// Noise offset was originally defined before VS parameter: "FrameRand" - algorithm untouched from original CryEngine implementation.
// Was moved to this location because for multi-pass the noise offset needs to be the same or there will be a mismatch.
static uint32 dwNoiseOffsetX = 0;
static uint32 dwNoiseOffsetY = 0;
dwNoiseOffsetX = (dwNoiseOffsetX + 27) & 0x3f;
dwNoiseOffsetY = (dwNoiseOffsetY + 19) & 0x3f;
Vec4 pFrameRand(
dwNoiseOffsetX / 64.0f,
dwNoiseOffsetX / 64.0f,
cry_random(0, 1023) / 1024.0f,
cry_random(0, 1023) / 1024.0f);
static ICVar* DolbyCvar = gEnv->pConsole->GetCVar("r_HDRDolby");
int DolbyCvarValue = DolbyCvar ? DolbyCvar->GetIVal() : eDVM_Disabled;
// Calculate dynamic metadata for Dolby vision if enabled.
if (DolbyCvarValue == eDVM_Vision && CRenderer::CV_r_HDRDolbyDynamicMetadata == 1)
{
CalculateDolbyDynamicMetadata(pSunShaftsRT);
}
{
CTexture* pBloom = CTextureManager::Instance()->GetBlackTexture();
if (CRenderer::CV_r_HDRBloom && CRenderer::CV_r_PostProcess)
{
pBloom = CTexture::s_ptexHDRFinalBloom;
}
PREFAST_ASSUME(pBloom);
CRY_ASSERT(pBloom);
uint32 nPasses;
static CCryNameTSCRC TechFinalName("HDRFinalPass");
static CCryNameTSCRC TechFinalDolbyName("HDRFinalPassDolby");
switch (DolbyCvarValue)
{
case eDVM_Disabled:
m_shHDR->FXSetTechnique(GetTonemapTechnique());
break;
case eDVM_RGBPQ:
case eDVM_Vision:
m_shHDR->FXSetTechnique(TechFinalDolbyName);
break;
}
m_shHDR->FXBegin(&nPasses, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
m_shHDR->FXBeginPass(0);
gcpRendD3D->FX_SetState(GS_NODEPTHTEST);
SetShaderParams();
// If any dolby output mode has been enabled, set required uniforms.
if (DolbyCvarValue >= eDVM_RGBPQ)
{
static CCryNameR pszHDRDolbyParam0("HDRDolbyScurveParams0");
static CCryNameR pszHDRDolbyParam1("HDRDolbyScurveParams1");
static CCryNameR pszHDRDolbyParam2("HDRDolbyScurveParams2");
Vec4 vHDRDolbyParam0(CRenderer::CV_r_HDRDolbyScurve ? 1.0f : 0.0f, CRenderer::CV_r_HDRDolbyScurveSourceMin, CRenderer::CV_r_HDRDolbyScurveSourceMid, CRenderer::CV_r_HDRDolbyScurveSourceMax);
Vec4 vHDRDolbyParam1(CRenderer::CV_r_HDRDolbyScurveRGBPQTargetMin, CRenderer::CV_r_HDRDolbyScurveRGBPQTargetMid, CRenderer::CV_r_HDRDolbyScurveRGBPQTargetMax, CRenderer::CV_r_HDRDolbyScurveSlope);
Vec4 vHDRDolbyParam2(CRenderer::CV_r_HDRDolbyDynamicMetadata ? 1.0f : 0.0f, 0.0f, 0.0f, CRenderer::CV_r_HDRDolbyScurveScale);
if (DolbyCvarValue == eDVM_Vision)
{
vHDRDolbyParam1.x = CRenderer::CV_r_HDRDolbyScurveVisionTargetMin;
vHDRDolbyParam1.y = CRenderer::CV_r_HDRDolbyScurveVisionTargetMid;
vHDRDolbyParam1.z = CRenderer::CV_r_HDRDolbyScurveVisionTargetMax;
}
m_shHDR->FXSetPSFloat(pszHDRDolbyParam0, &vHDRDolbyParam0, 1);
m_shHDR->FXSetPSFloat(pszHDRDolbyParam1, &vHDRDolbyParam1, 1);
m_shHDR->FXSetPSFloat(pszHDRDolbyParam2, &vHDRDolbyParam2, 1);
}
static CCryNameR pSunShaftsParamSName("SunShafts_SunCol");
Vec4 pShaftsSunCol(0, 0, 0, 0);
if (pSunShaftsRT)
{
Vec4 pSunShaftsParams[2];
pSunShaftsTech->GetSunShaftsParams(pSunShaftsParams);
Vec3 pSunColor = gEnv->p3DEngine->GetSunColor();
pSunColor.Normalize();
pSunColor.SetLerp(Vec3(pSunShaftsParams[0].x, pSunShaftsParams[0].y, pSunShaftsParams[0].z), pSunColor, pSunShaftsParams[1].w);
pShaftsSunCol = Vec4(pSunColor * pSunShaftsParams[1].z, 1);
}
m_shHDR->FXSetPSFloat(pSunShaftsParamSName, &pShaftsSunCol, 1);
// Force commit before setting samplers - workaround for per frame samplers hardcoded/overriding sampler slots.
rd->FX_Commit();
CTexture::s_ptexHDRTarget->Apply(static_cast<int>(eHDRPostProcessSRVs::HDRInput), nTexStateLinear, EFTT_UNKNOWN, -1, SResourceView::DefaultView);
if (CTexture::s_ptexCurLumTexture)
{
if (!gRenDev->m_CurViewportID)
{
CTexture::s_ptexCurLumTexture->Apply(static_cast<int>(eHDRPostProcessSRVs::Luminance), nTexStateLinear /*nTexStatePoint*/);
}
else
{
CTexture::s_ptexHDRToneMaps[0]->Apply(static_cast<int>(eHDRPostProcessSRVs::Luminance), nTexStateLinear);
}
}
pBloom->Apply(static_cast<int>(eHDRPostProcessSRVs::Bloom), nTexStateLinear);
if (!gcpRendD3D->FX_GetEnabledGmemPath(nullptr))
{
CTexture::s_ptexVelocity->Apply(static_cast<int>(eHDRPostProcessSRVs::Velocity), nTexStatePoint);
}
else
{
CTexture::s_ptexZTarget->Apply(static_cast<int>(eHDRPostProcessSRVs::ZTarget), nTexStatePoint);
}
AZ_PUSH_DISABLE_WARNING(, "-Wconstant-logical-operand")
if (CRenderer::CV_r_PostProcess && CRenderer::CV_r_HDRVignetting)
AZ_POP_DISABLE_WARNING
{
CTextureManager::Instance()->GetDefaultTexture("VignettingMap")->Apply(static_cast<int>(eHDRPostProcessSRVs::VignetteMap), nTexStateLinear);
}
else
{
CTexture* pWhite = CTextureManager::Instance()->GetWhiteTexture();
pWhite->Apply(static_cast<int>(eHDRPostProcessSRVs::VignetteMap), nTexStateLinear);
}
if (pTexColorChar)
{
pTexColorChar->Apply(static_cast<int>(eHDRPostProcessSRVs::ColorChar), nTexStateLinear);
}
if (pSunShaftsRT)
{
pSunShaftsRT->Apply(static_cast<int>(eHDRPostProcessSRVs::SunShafts), nTexStateLinear);
}
SD3DPostEffectsUtils::DrawFullScreenTriWPOS(CTexture::s_ptexBackBuffer->GetWidth(), CTexture::s_ptexBackBuffer->GetHeight());
}
// Reset don't care actions for UI and other passes after post-proc pipeline
rd->FX_SetColorDontCareActions(0, false, false);
}
void CHDRPostProcess::EncodeDolbyVision(CTexture* source)
{
CD3D9Renderer* rd = gcpRendD3D;
SRenderPipeline& RESTRICT_REFERENCE rRP = rd->m_RP;
int NumPasses = 2;
for (int pass = 0; pass < NumPasses; pass++)
{
uint32 nPasses;
static CCryNameTSCRC TechFinalDolbyVisionName("HDRFinalPassDolbyVision");
static CCryNameTSCRC TechFinalDolbyVisionNoMetadataName("HDRFinalPassDolbyVisionNoMetadata");
if (pass == 0)
{
m_shHDR->FXSetTechnique(TechFinalDolbyVisionName);
}
else if (pass == 1)
{
m_shHDR->FXSetTechnique(TechFinalDolbyVisionNoMetadataName);
}
m_shHDR->FXBegin(&nPasses, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
m_shHDR->FXBeginPass(0);
gcpRendD3D->FX_SetState(GS_NODEPTHTEST);
// Set all shader params
SetShaderParams();
// If any dolby output mode has been enabled, set required uniforms.
{
float width = (float)rd->GetOverlayWidth();
float height = (float)rd->GetOverlayHeight();
static CCryNameR pszHDRDolbyParam0("HDRDolbyScurveParams0");
static CCryNameR pszHDRDolbyParam1("HDRDolbyScurveParams1");
static CCryNameR pszHDRDolbyParam2("HDRDolbyScurveParams2");
Vec4 vHDRDolbyParam0(CRenderer::CV_r_HDRDolbyScurve ? 1.0f : 0.0f, CRenderer::CV_r_HDRDolbyScurveSourceMin, CRenderer::CV_r_HDRDolbyScurveSourceMid, CRenderer::CV_r_HDRDolbyScurveSourceMax);
Vec4 vHDRDolbyParam1(CRenderer::CV_r_HDRDolbyScurveVisionTargetMin, CRenderer::CV_r_HDRDolbyScurveVisionTargetMid, CRenderer::CV_r_HDRDolbyScurveVisionTargetMax, CRenderer::CV_r_HDRDolbyScurveSlope);
Vec4 vHDRDolbyParam2(CRenderer::CV_r_HDRDolbyDynamicMetadata ? 1.0f : 0.0f, 0.0, 0.0, CRenderer::CV_r_HDRDolbyScurveScale);
m_shHDR->FXSetPSFloat(pszHDRDolbyParam0, &vHDRDolbyParam0, 1);
m_shHDR->FXSetPSFloat(pszHDRDolbyParam1, &vHDRDolbyParam1, 1);
m_shHDR->FXSetPSFloat(pszHDRDolbyParam2, &vHDRDolbyParam2, 1);
}
// Force commit before setting samplers - workaround for per frame samplers hardcoded/overriding sampler slots.
rd->FX_Commit();
SResourceView::KeyType nResourceID = SResourceView::DefaultView;
source->Apply(static_cast<int>(eHDRPostProcessSRVs::HDRInput), nTexStateLinear, EFTT_UNKNOWN, -1, nResourceID);
// Provide dynamic metadata values to the shader if enabled.
if (CRenderer::CV_r_HDRDolbyDynamicMetadata == 1)
{
rd->m_DevMan.BindSRV(eHWSC_Pixel, m_bufDolbyMetadataMinMaxMid.GetShaderResourceView(), static_cast<int>(eHDRPostProcessSRVs::DolbyVisionDynamicMeta));
}
{
// Dolby Vision split rendering (metadata/non-metadata).
// * Calculate split boundaries.
// Metadata needs 128 bytes * 3 times (according to dolby 3x repeater spec).
// Every pixel stores 1 bit so at least 3072 pixels are needed to store the metadata. 4000 is a safe margin above that value.
float pixelsNeeded = 4000.0f;
float rowsNeeded = pixelsNeeded / rd->GetBackbufferWidth();
float pixelRows = ceil(rowsNeeded);
float pixelRowsNormalized = pixelRows / rd->GetBackbufferHeight();
// * Draw split quad.
Vec2 srcLeftTop(0.0f, 0.0f), srcRightBottom(1.0f, 1.0f);
if (pass == 0)
{
srcRightBottom.y = pixelRowsNormalized; // Draw top half with metadata.
}
else
{
srcLeftTop.y = pixelRowsNormalized; // Draw bottom half without metadata.
}
SD3DPostEffectsUtils::DrawQuad(-1, -1, Vec2(srcLeftTop.x, srcLeftTop.y), Vec2(srcLeftTop.x, srcRightBottom.y), Vec2(srcRightBottom.x, srcRightBottom.y), Vec2(srcRightBottom.x, srcLeftTop.y),
Vec2(srcLeftTop.x, srcLeftTop.y), Vec2(srcLeftTop.x, srcRightBottom.y), Vec2(srcRightBottom.x, srcRightBottom.y), Vec2(srcRightBottom.x, srcLeftTop.y));
}
}
}
CCryNameTSCRC CHDRPostProcess::GetTonemapTechnique() const
{
ToneMapOperators toneMapTech = static_cast<ToneMapOperators>(CRenderer::CV_r_ToneMapTechnique);
switch(toneMapTech)
{
case ToneMapOperators::Linear:
return CCryNameTSCRC("HDRToneMapLinear");
case ToneMapOperators::Exponential:
return CCryNameTSCRC("HDRToneMapExponential");
case ToneMapOperators::Reinhard:
return CCryNameTSCRC("HDRToneMapReinhard");
case ToneMapOperators::FilmicCurveALU:
return CCryNameTSCRC("HDRToneMapFilmicALU");
case ToneMapOperators::FilmicCurveUC2:
return CCryNameTSCRC("HDRFinalPass");
default:
{
CryWarning(VALIDATOR_MODULE_RENDERER, VALIDATOR_WARNING, "Tonemap technique not supported");
return CCryNameTSCRC("HDRFinalPass");
}
}
}
void CHDRPostProcess::SetExposureTypeShaderFlags()
{
gRenDev->m_RP.m_FlagsShader_RT &= ~(g_HWSR_MaskBit[HWSR_SAMPLE5]);
Exposuretype expType = static_cast<Exposuretype>(CRenderer::CV_r_ToneMapExposureType);
switch(expType)
{
case Exposuretype::Auto:
{
gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE5];
return;
}
case Exposuretype::Manual:
{
//Don't need to do anything as it will default to manual.
return;
}
default:
{
CryWarning(VALIDATOR_MODULE_RENDERER, VALIDATOR_WARNING, "Exposure type not supported");
}
}
}
void CHDRPostProcess::ToneMappingDebug()
{
CD3D9Renderer* rd = gcpRendD3D;
Vec4 avSampleOffsets[4];
PROFILE_LABEL_SCOPE("TONEMAPPINGDEBUG");
// Enable corresponding shader variation
gRenDev->m_RP.m_FlagsShader_RT &= ~(g_HWSR_MaskBit[HWSR_SAMPLE4] | g_HWSR_MaskBit[HWSR_SAMPLE5]);
if (CRenderer::CV_r_HDRDebug == 1)
{
gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_DEBUG0];
}
else
{
gRenDev->m_RP.m_FlagsShader_RT &= ~g_HWSR_MaskBit[HWSR_DEBUG0];
}
if (CRenderer::CV_r_HDREyeAdaptationMode == 2)
{
gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE4];
}
SetExposureTypeShaderFlags();
uint32 nPasses;
static CCryNameTSCRC techName("HDRFinalDebugPass");
m_shHDR->FXSetTechnique(techName);
m_shHDR->FXBegin(&nPasses, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
m_shHDR->FXBeginPass(0);
GetSampleOffsets_DownScale2x2(CTexture::s_ptexHDRTarget->GetWidth(), CTexture::s_ptexHDRTarget->GetHeight(), avSampleOffsets);
static CCryNameR SampleOffsetsName("SampleOffsets");
m_shHDR->FXSetPSFloat(SampleOffsetsName, avSampleOffsets, 4);
SetShaderParams();
CTexture::s_ptexHDRTarget->Apply(0, nTexStatePoint);
CTexture::s_ptexHDRToneMaps[0]->Apply(1, nTexStateLinear);
DrawFullScreenQuad(0.0f, 0.0f, 1.0f, 1.0f);
gRenDev->m_RP.m_FlagsShader_RT &= ~g_HWSR_MaskBit[HWSR_DEBUG0];
}
void CHDRPostProcess::CalculateDolbyDynamicMetadata(CTexture* pSunShaftsRT)
{
PROFILE_LABEL_SCOPE("DOLBY_DYNAMIC_META");
CD3D9Renderer* rd = &gcpRendD3D;
// Settings (must match shaders!)
int width = gRenDev->GetBackbufferWidth();
int height = gRenDev->GetBackbufferHeight();
int pass0ReductionX = 16;
int pass0ReductionY = 16;
int pass0ThreadsX = 16;
int pass0ThreadsY = 16;
int pass0StrideX = 2;
int pass0StrideY = 2;
double outWidth = width / static_cast<double>(pass0ReductionX * pass0StrideX);
double outHeight = height / static_cast<double>(pass0ReductionY * pass0StrideY);
double dispatchX = width / static_cast<double>(pass0ReductionX * pass0ThreadsX * pass0StrideX);
double dispatchY = height / static_cast<double>(pass0ReductionY * pass0ThreadsY * pass0StrideY);
dispatchX = ceil(dispatchX);
dispatchY = ceil(dispatchY);
outWidth = ceil(outWidth);
outHeight = ceil(outHeight);
// Make sure StructuredBuffers are initialized..
if (m_bufDolbyMetadataMacroReductionOutput.m_pBuffer == nullptr)
{
// Max resolution 4K (4096x2160)
m_bufDolbyMetadataMacroReductionOutput.Create((4096 * 2160) / (pass0ReductionX * pass0ReductionY), sizeof(float) * 4, DXGI_FORMAT_UNKNOWN, DX11BUF_STRUCTURED | DX11BUF_BIND_UAV | DX11BUF_BIND_SRV, nullptr);
m_bufDolbyMetadataMinMaxMid.Create(3, sizeof(float), DXGI_FORMAT_UNKNOWN, DX11BUF_STRUCTURED | DX11BUF_BIND_UAV | DX11BUF_BIND_SRV, nullptr);
}
// Make sure shaders are loaded..
if (m_shHDRDolbyMetadataPass0 == nullptr)
{
m_shHDRDolbyMetadataPass0 = gcpRendD3D->m_cEF.mfForName("HDRDolbyMetadataPass0", EF_SYSTEM);
m_shHDRDolbyMetadataPass1 = gcpRendD3D->m_cEF.mfForName("HDRDolbyMetadataPass1", EF_SYSTEM);
}
// Shared variables.
uint32 nPasses;
Vec4 parameters[2];
static CCryNameR Parameter0Name("Parameters0");
static CCryNameR Parameter1Name("Parameters1");
// Pass 1: Macro reduction of HDR signal. (256x reduction)
parameters[0] = Vec4(0.0f, 0.0f, static_cast<float>(width), static_cast<float>(height));
parameters[1] = Vec4(static_cast<float>(outWidth), 0.0f, 0.0f, 0.0f);
m_shHDRDolbyMetadataPass0->FXSetTechnique("Default");
m_shHDRDolbyMetadataPass0->FXBegin(&nPasses, 0);
m_shHDRDolbyMetadataPass0->FXBeginPass(0);
m_shHDRDolbyMetadataPass0->FXSetCSFloat(Parameter0Name, parameters, 1);
m_shHDRDolbyMetadataPass0->FXSetCSFloat(Parameter1Name, parameters + 1, 1);
rd->FX_Commit();
// Bind HDR targets in order to recreating the same HDR input signal.
CTexture::s_ptexHDRTarget->Apply(0, nTexStateLinear, EFTT_UNKNOWN, -1, SResourceView::DefaultView, eHWSC_Compute);
CTexture::s_ptexHDRToneMaps[0]->Apply(1, nTexStateLinear, EFTT_UNKNOWN, -1, SResourceView::DefaultView, eHWSC_Compute);
CTexture* pBloom = CTextureManager::Instance()->GetBlackTexture();
if (CRenderer::CV_r_HDRBloom && CRenderer::CV_r_PostProcess)
{
pBloom = CTexture::s_ptexHDRFinalBloom;
}
pBloom->Apply(2, nTexStateLinear, EFTT_UNKNOWN, -1, SResourceView::DefaultView, eHWSC_Compute);
if (pSunShaftsRT)
{
pSunShaftsRT->Apply(9, nTexStateLinear, EFTT_UNKNOWN, -1, SResourceView::DefaultView, eHWSC_Compute);
}
rd->m_DevMan.BindUAV(eHWSC_Compute, m_bufDolbyMetadataMacroReductionOutput.GetUnorderedAccessView(), 0, 0);
// Execute pass 1.
rd->m_DevMan.Dispatch(static_cast<int>(dispatchX), static_cast<int>(dispatchY), 1);
m_shHDRDolbyMetadataPass0->FXEndPass();
m_shHDRDolbyMetadataPass0->FXEnd();
// Unbind UAV.
rd->m_DevMan.BindUAV(eHWSC_Compute, nullptr, 0, 0);
rd->m_DevMan.CommitDeviceStates();
// Pass 2: Micro reduction. (remainder)
parameters[0] = Vec4(static_cast<float>(outWidth * outHeight), 0.0f, 0.0f, 0.0f);
m_shHDRDolbyMetadataPass1->FXSetTechnique("Default");
m_shHDRDolbyMetadataPass1->FXBegin(&nPasses, 0);
m_shHDRDolbyMetadataPass1->FXBeginPass(0);
m_shHDRDolbyMetadataPass1->FXSetCSFloat(Parameter0Name, parameters, 1);
rd->FX_Commit();
rd->m_DevMan.BindUAV(eHWSC_Compute, m_bufDolbyMetadataMinMaxMid.GetUnorderedAccessView(), 0, 0); // Unbind previous UAV.
rd->m_DevMan.BindSRV(eHWSC_Compute, m_bufDolbyMetadataMacroReductionOutput.GetShaderResourceView(), 0);
// Execute pass 2.
rd->m_DevMan.Dispatch(1, 1, 1);
m_shHDRDolbyMetadataPass1->FXEndPass();
m_shHDRDolbyMetadataPass1->FXEnd();
// Unbind UAV.
rd->m_DevMan.BindUAV(eHWSC_Compute, nullptr, 0, 0);
rd->m_DevMan.CommitDeviceStates();
}
void CHDRPostProcess::DrawDebugViews()
{
if (CRenderer::CV_r_HDRDebug != 1 && CRenderer::CV_r_HDRDebug != 3 && CRenderer::CV_r_HDRDebug != 4)
{
return;
}
CD3D9Renderer* rd = gcpRendD3D;
uint32 nPasses = 0;
if (CRenderer::CV_r_HDRDebug == 1)
{
// We use screen shots to create minimaps, and we don't want to
// have any debug text on minimaps.
{
ICVar* const pVar = gEnv->pConsole->GetCVar("e_ScreenShot");
if (pVar && pVar->GetIVal() != 0)
{
return;
}
}
STALL_PROFILER("read scene luminance")
float fLuminance = -1;
float fIlluminance = -1;
CDeviceTexture* pSrcDevTex = CTexture::s_ptexHDRToneMaps[0]->GetDevTexture();
pSrcDevTex->DownloadToStagingResource(0, [&](void* pData, [[maybe_unused]] uint32 rowPitch, [[maybe_unused]] uint32 slicePitch)
{
CryHalf* pRawPtr = reinterpret_cast<CryHalf*>(pData);
fLuminance = CryConvertHalfToFloat(pRawPtr[0]);
fIlluminance = CryConvertHalfToFloat(pRawPtr[1]);
return true;
});
char str[256];
SDrawTextInfo ti;
ti.color[1] = 0;
azsprintf(str, "Average Luminance (cd/m2): %.2f", fLuminance * RENDERER_LIGHT_UNIT_SCALE);
rd->Draw2dText(5, 35, str, ti);
azsprintf(str, "Estimated Illuminance (lux): %.1f", fIlluminance * RENDERER_LIGHT_UNIT_SCALE);
rd->Draw2dText(5, 55, str, ti);
Vec4 vHDRSetupParams[5];
gEnv->p3DEngine->GetHDRSetupParams(vHDRSetupParams);
if (CRenderer::CV_r_HDREyeAdaptationMode == 2)
{
// Compute scene key and exposure in the same way as in the tone mapping shader
float sceneKey = 1.03f - 2.0f / (2.0f + log(fLuminance + 1.0f) / log(2.0f));
float exposure = clamp_tpl<float>(sceneKey / fLuminance, vHDRSetupParams[4].y, vHDRSetupParams[4].z);
azsprintf(str, "Exposure: %.2f SceneKey: %.2f", exposure, sceneKey);
rd->Draw2dText(5, 75, str, ti);
}
else
{
float exposure = log(fIlluminance * RENDERER_LIGHT_UNIT_SCALE * 100.0f / 330.0f) / log(2.0f);
float sceneKey = log(fIlluminance * RENDERER_LIGHT_UNIT_SCALE + 1.0f) / log(10.0f);
float autoCompensation = (clamp_tpl<float>(sceneKey, 0.1f, 5.2f) - 3.0f) / 2.0f * vHDRSetupParams[3].z;
float finalExposure = clamp_tpl<float>(exposure - autoCompensation, vHDRSetupParams[3].x, vHDRSetupParams[3].y);
azsprintf(str, "Measured EV: %.1f Auto-EC: %.1f Final EV: %.1f", exposure, autoCompensation, finalExposure);
rd->Draw2dText(5, 75, str, ti);
}
return;
}
rd->FX_SetState(GS_NODEPTHTEST);
int iTmpX, iTmpY, iTempWidth, iTempHeight;
rd->GetViewport(&iTmpX, &iTmpY, &iTempWidth, &iTempHeight);
rd->EF_SetColorOp(eCO_MODULATE, eCO_MODULATE, DEF_TEXARG0, DEF_TEXARG0);
rd->EF_SetSrgbWrite(false);
TransformationMatrices backupSceneMatrices;
rd->Set2DMode(1, 1, backupSceneMatrices);
gRenDev->m_cEF.mfRefreshSystemShader("Debug", CShaderMan::s_ShaderDebug);
CShader* pSH = CShaderMan::s_ShaderDebug;
CTexture* pSceneTargetHalfRes = CTexture::s_ptexHDRTargetScaled[0];
CTexture::s_ptexHDRTarget->SetResolved(false);
CTexture::s_ptexHDRTarget->Resolve();
// 1
uint32 nPosX = 10;
rd->RT_SetViewport(nPosX, 500, 100, 100);
rd->DrawImage(0, 0, 1, 1, CTexture::s_ptexHDRTarget->GetID(), 0, 1, 1, 0, 1, 1, 1, 1);
// 2
rd->RT_SetViewport(nPosX += 110, 500, 100, 100);
rd->DrawImage(0, 0, 1, 1, pSceneTargetHalfRes->GetID(), 0, 1, 1, 0, 1, 1, 1, 1);
// 3
if (CRenderer::CV_r_HDRBloom)
{
// Bloom generation/intermediate render-targets
// Quarter res
rd->RT_SetViewport(nPosX += 110, 500, 100, 100);
rd->DrawImage(0, 0, 1, 1, CTexture::s_ptexHDRTempBloom[0]->GetID(), 0, 1, 1, 0, 1, 1, 1, 1);
rd->RT_SetViewport(nPosX += 110, 500, 100, 100);
rd->DrawImage(0, 0, 1, 1, CTexture::s_ptexHDRTempBloom[1]->GetID(), 0, 1, 1, 0, 1, 1, 1, 1);
rd->RT_SetViewport(nPosX += 110, 500, 100, 100);
rd->DrawImage(0, 0, 1, 1, CTexture::s_ptexHDRFinalBloom->GetID(), 0, 1, 1, 0, 1, 1, 1, 1);
}
nPosX = 10;
pSH->FXSetTechnique("Debug_ShowR");
pSH->FXBegin(&nPasses, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
pSH->FXBeginPass(0);
CTexture::s_ptexHDRToneMaps[3]->Apply(0, nTexStatePoint);
rd->RT_SetViewport(nPosX, 610, 100, 100);
DrawFullScreenQuadTR(0.0f, 0.0f, 1.0f, 1.0f);
if (CTexture::s_ptexHDRToneMaps[2])
{
CTexture::s_ptexHDRToneMaps[2]->Apply(0, nTexStatePoint);
rd->RT_SetViewport(nPosX += 110, 610, 100, 100);
DrawFullScreenQuadTR(0.0f, 0.0f, 1.0f, 1.0f);
}
if (CTexture::s_ptexHDRToneMaps[1])
{
CTexture::s_ptexHDRToneMaps[1]->Apply(0, nTexStatePoint);
rd->RT_SetViewport(nPosX += 110, 610, 100, 100);
DrawFullScreenQuadTR(0.0f, 0.0f, 1.0f, 1.0f);
}
if (CTexture::s_ptexHDRToneMaps[0])
{
CTexture::s_ptexHDRToneMaps[0]->Apply(0, nTexStatePoint);
rd->RT_SetViewport(nPosX += 110, 610, 100, 100);
DrawFullScreenQuadTR(0.0f, 0.0f, 1.0f, 1.0f);
}
if (CTexture::s_ptexCurLumTexture)
{
CTexture::s_ptexCurLumTexture->Apply(0, nTexStatePoint);
rd->RT_SetViewport(nPosX += 110, 610, 100, 100);
DrawFullScreenQuadTR(0.0f, 0.0f, 1.0f, 1.0f);
}
pSH->FXEndPass();
pSH->FXEnd();
rd->Unset2DMode(backupSceneMatrices);
rd->RT_SetViewport(iTmpX, iTmpY, iTempWidth, iTempHeight);
{
char str[256];
SDrawTextInfo ti;
sprintf_s(str, "HDR rendering debug");
rd->Draw2dText(5, 310, str, ti);
}
}
void CHDRPostProcess::ScreenShot()
{
if (CRenderer::CV_r_GetScreenShot == 1)
{
iLog->LogError("HDR screen shots are not yet supported on DX11!"); // TODO: D3DXSaveSurfaceToFile()
}
}
void CHDRPostProcess::Begin()
{
gcpRendD3D->GetModelViewMatrix(PostProcessUtils().m_pView.GetData());
gcpRendD3D->GetProjectionMatrix(PostProcessUtils().m_pProj.GetData());
// Store some commonly used per-frame data
PostProcessUtils().m_pViewProj = PostProcessUtils().m_pView * PostProcessUtils().m_pProj;
if (gcpRendD3D->m_RP.m_TI[gcpRendD3D->m_RP.m_nProcessThreadID].m_PersFlags & RBPF_REVERSE_DEPTH)
{
PostProcessUtils().m_pViewProj = ReverseDepthHelper::Convert(PostProcessUtils().m_pViewProj);
}
PostProcessUtils().m_pViewProj.Transpose();
m_shHDR = CShaderMan::s_shHDRPostProcess;
if (CTexture::s_ptexHDRTarget->GetWidth() != gcpRendD3D->GetWidth() || CTexture::s_ptexHDRTarget->GetHeight() != gcpRendD3D->GetHeight())
{
CTexture::GenerateHDRMaps();
}
gcpRendD3D->FX_ResetPipe();
PostProcessUtils().SetFillModeSolid(true);
}
void CHDRPostProcess::End()
{
gcpRendD3D->m_RP.m_TI[gcpRendD3D->m_RP.m_nProcessThreadID].m_PersFlags &= ~RBPF_HDR;
gcpRendD3D->m_RP.m_PersFlags2 &= ~(RBPF2_HDR_FP16 | RBPF2_LIGHTSHAFTS);
gcpRendD3D->FX_ResetPipe();
PostProcessUtils().SetFillModeSolid(false);
// (re-set back-buffer): if the platform does lazy RT updates/setting there's strong possibility we run into problems when we try to resolve with no RT set
gcpRendD3D->FX_SetActiveRenderTargets();
}
void CHDRPostProcess::Render()
{
PROFILE_LABEL_SCOPE("HDR_POSTPROCESS");
Begin();
if (!gcpRendD3D->FX_GetEnabledGmemPath(nullptr))
{
CRY_ASSERT(gcpRendD3D->FX_GetCurrentRenderTarget(0) == CTexture::s_ptexHDRTarget);
gcpRendD3D->FX_SetActiveRenderTargets(); // Called explicitly to work around RT stack problems on deprecated platform
gcpRendD3D->RT_UnbindTMUs();// Avoid d3d error due to potential rtv still bound as shader input.
gcpRendD3D->FX_PopRenderTarget(0);
gcpRendD3D->EF_ClearTargetsLater(0);
}
// Skip hdr/post processing when rendering different camera views
if ((gcpRendD3D->m_RP.m_TI[gcpRendD3D->m_RP.m_nProcessThreadID].m_PersFlags & RBPF_MIRRORCULL) || (gcpRendD3D->m_RP.m_nRendFlags & SHDF_CUBEMAPGEN))
{
End();
return;
}
#if !defined(_RELEASE) || defined(WIN32) || defined(WIN64) || defined(ENABLE_LW_PROFILERS)
ScreenShot();
#endif
gcpRendD3D->m_RP.m_FlagsShader_RT = 0;
gcpRendD3D->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE0]; // enable srgb. Can save this flag, always enabled
gcpRendD3D->FX_ApplyShaderQuality(eST_PostProcess);
m_bHiQuality = CPostEffectsMgr::CheckPostProcessQuality(eRQ_Low, eSQ_VeryHigh);
static ICVar* DolbyCvar = gEnv->pConsole->GetCVar("r_HDRDolby");
const int DolbyCvarValue = DolbyCvar ? DolbyCvar->GetIVal() : eDVM_Disabled;
const bool bDolbyHDRMode = DolbyCvarValue > eDVM_Disabled;
if (CRenderer::CV_r_PostProcess)
{
CStandardGraphicsPipeline& graphicsPipeline = gcpRendD3D->GetGraphicsPipeline();
const bool bSolidModeEnabled = gcpRendD3D->GetWireframeMode() == R_SOLID_MODE;
const bool bDepthOfFieldEnabled = CRenderer::CV_r_dof >= 1 && bSolidModeEnabled;
const bool takingScreenShot = (gcpRendD3D->m_screenShotType != 0);
const bool bMotionBlurEnabled = CRenderer::CV_r_MotionBlur && bSolidModeEnabled && (!takingScreenShot || CRenderer::CV_r_MotionBlurScreenShot) && !CRenderer::CV_r_RenderMotionBlurAfterHDR;
DepthOfFieldParameters depthOfFieldParameters;
if (bDepthOfFieldEnabled)
{
CDepthOfField* depthOfField = static_cast<CDepthOfField*>(PostEffectMgr()->GetEffect(ePFX_eDepthOfField));
depthOfField->UpdateParameters();
depthOfFieldParameters = depthOfField->GetParameters();
}
if (CRenderer::CV_r_AntialiasingMode == eAT_TAA)
{
CRY_ASSERT_MESSAGE(CRenderer::CV_r_ToneMapExposureType==static_cast<int>(Exposuretype::Auto), "TAA needs auto exposure");
GetUtils().StretchRect(CTexture::s_ptexHDRTarget, CTexture::s_ptexSceneTarget);
graphicsPipeline.RenderTemporalAA(CTexture::s_ptexSceneTarget, CTexture::s_ptexHDRTarget, depthOfFieldParameters);
}
// Rain
CSceneRain* pSceneRain = static_cast<CSceneRain*>(PostEffectMgr()->GetEffect(ePFX_SceneRain));
const SRainParams& rainInfo = gcpRendD3D->m_p3DEngineCommon.m_RainInfo;
if (pSceneRain && pSceneRain->IsActive() && rainInfo.fRainDropsAmount > 0.01f)
{
pSceneRain->Render();
}
// Motion blur not enabled in 256bpp GMEM paths
if (CD3D9Renderer::eGT_256bpp_PATH != gcpRendD3D->FX_GetEnabledGmemPath(nullptr))
{
// Note: Motion blur uses s_ptexHDRTargetPrev to avoid doing another copy, so this should be right before the MB pass
PostProcessUtils().StretchRect(CTexture::s_ptexHDRTarget, CTexture::s_ptexHDRTargetPrev, false, false, false, false, SPostEffectsUtils::eDepthDownsample_None, false, &gcpRendD3D->m_FullResRect);
}
if (bDepthOfFieldEnabled)
{
graphicsPipeline.RenderDepthOfField();
}
uint32 flags = SRendItem::BatchFlags(EFSLIST_TRANSP, gcpRendD3D->m_RP.m_pRLD);
if (flags & FB_TRANSPARENT_AFTER_DOF)
{
PROFILE_LABEL_SCOPE("PARTICLES AFTER DOF");
//render (after water) transparent particles list which was set to skip Depth of Field
gcpRendD3D->FX_PushRenderTarget(0, CTexture::s_ptexHDRTarget, &gcpRendD3D->m_DepthBufferOrig);
uint32 nBatchFilter = FB_TRANSPARENT_AFTER_DOF;
gcpRendD3D->FX_ProcessRenderList(EFSLIST_TRANSP, 1, gcpRendD3D->FX_FlushShader_General, true, nBatchFilter);
gcpRendD3D->FX_PopRenderTarget(0);
}
if (bMotionBlurEnabled)
{
// Added old pipeline render call here. This lets us do motion blur before the end of HDR processing.
if (CRenderer::CV_r_GraphicsPipeline > 0)
{
graphicsPipeline.RenderMotionBlur();
}
else
{
CMotionBlur* motionBlurEffect = static_cast<CMotionBlur*>(PostEffectMgr()->GetEffect(ePFX_eMotionBlur));
if (motionBlurEffect)
{
motionBlurEffect->Render();
}
}
}
{
CSceneSnow* pSceneSnow = static_cast<CSceneSnow*>(PostEffectMgr()->GetEffect(ePFX_SceneSnow));
if (pSceneSnow->IsActiveSnow())
{
pSceneSnow->Render();
}
}
//Render passes for auto exposure. Used for tonemapping or Bloom generation
if (CRenderer::CV_r_ToneMapExposureType == static_cast<int>(Exposuretype::Auto) || CRenderer::CV_r_HDRBloom)
{
HalfResDownsampleHDRTarget();
gcpRendD3D->SetCurDownscaleFactor(Vec2(1, 1));
QuarterResDownsampleHDRTarget();
gcpRendD3D->FX_ApplyShaderQuality(eST_PostProcess);
// Update eye adaptation
if (CRenderer::CV_r_EnableGMEMPostProcCS)
{
MeasureLumEyeAdaptationUsingCompute();
}
else
{
MeasureLuminance();
EyeAdaptation();
}
}
if (CRenderer::CV_r_HDRBloom)
{
BloomGeneration();
}
}
gcpRendD3D->SetCurDownscaleFactor(gcpRendD3D->m_CurViewportScale);
bool postAAWillApplyAA = (CRenderer::CV_r_AntialiasingMode == eAT_SMAA1TX) || (CRenderer::CV_r_AntialiasingMode == eAT_FXAA);
bool shouldRenderToBackbufferNow = CRenderer::CV_r_SkipNativeUpscale && CRenderer::CV_r_SkipRenderComposites && !postAAWillApplyAA;
if (shouldRenderToBackbufferNow)
{
gcpRendD3D->RT_SetViewport(0, 0, gcpRendD3D->GetNativeWidth(), gcpRendD3D->GetNativeHeight());
gcpRendD3D->FX_SetRenderTarget(0, gcpRendD3D->GetBackBuffer(), nullptr);
gcpRendD3D->FX_SetActiveRenderTargets();
}
else
{
gcpRendD3D->FX_PushRenderTarget(0, SPostEffectsUtils::AcquireFinalCompositeTarget(bDolbyHDRMode), &gcpRendD3D->m_DepthBufferOrigMSAA);
}
// Render final scene to the back buffer
if (CRenderer::CV_r_HDRDebug != 1 && CRenderer::CV_r_HDRDebug != 2)
{
ToneMapping();
}
else
{
ToneMappingDebug();
}
if (CRenderer::CV_r_HDRDebug > 0)
{
DrawDebugViews();
}
End();
}
void CD3D9Renderer::FX_HDRPostProcessing()
{
PROFILE_FRAME(Draw_HDR_PostProcessing);
if (gcpRendD3D->m_bDeviceLost)
{
return;
}
if (!CTexture::IsTextureExist(CTexture::s_ptexHDRTarget))
{
return;
}
CHDRPostProcess* pHDRPostProcess = CHDRPostProcess::GetInstance();
pHDRPostProcess->Render();
pHDRPostProcess->ProcessLensOptics();
}
void CD3D9Renderer::FX_FinalComposite()
{
CTexture* upscaleSource = SPostEffectsUtils::GetFinalCompositeTarget();
if (FX_GetCurrentRenderTarget(0) == upscaleSource && upscaleSource != nullptr)
{
FX_PopRenderTarget(0);
#if AZ_RENDER_TO_TEXTURE_GEM_ENABLED
const bool isRenderSceneToTexture = (m_RP.m_TI[m_RP.m_nProcessThreadID].m_PersFlags & RBPF_RENDER_SCENE_TO_TEXTURE) != 0;
if (!isRenderSceneToTexture)
{
RT_SetViewport(0, 0, m_nativeWidth, m_nativeHeight);
FX_SetRenderTarget(0, m_pBackBuffer, nullptr);
FX_SetActiveRenderTargets();
}
#else
RT_SetViewport(0, 0, m_nativeWidth, m_nativeHeight);
FX_SetRenderTarget(0, m_pBackBuffer, nullptr);
FX_SetActiveRenderTargets();
#endif // if AZ_RENDER_TO_TEXTURE_GEM_ENABLED
static ICVar* DolbyCvar = gEnv->pConsole->GetCVar("r_HDRDolby");
int DolbyCvarValue = DolbyCvar ? DolbyCvar->GetIVal() : eDVM_Disabled;
#if AZ_RENDER_TO_TEXTURE_GEM_ENABLED
if (DolbyCvarValue == eDVM_Vision && !isRenderSceneToTexture)
#else
if (DolbyCvarValue == eDVM_Vision)
#endif // if AZ_RENDER_TO_TEXTURE_GEM_ENABLED
{
CHDRPostProcess::GetInstance()->EncodeDolbyVision(upscaleSource);
}
else
{
GetGraphicsPipeline().RenderFinalComposite(upscaleSource);
}
}
}