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/GraphicsPipeline/DepthOfField.cpp

419 lines
17 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 "DepthOfField.h"
#include "DriverD3D.h"
#include "D3DPostProcess.h"
#include "../../Common/Textures/TextureManager.h"
#include "Common/RenderCapabilities.h"
namespace
{
const float Pi = (float)M_PI;
float ngon_rad(float theta, float n)
{
return cosf(Pi / n) / cosf(theta - (2 * Pi / n) * floorf((n * theta + PI) / (2 * Pi)));
}
// Shirleys concentric mapping
static Vec2 ToUnitDisk(Vec2 O, float blades, [[maybe_unused]] float fstop)
{
float max_fstops = 8;
float min_fstops = 1;
float normalizedStops = 1.0f;
float phi, r;
const float a = 2 * O.x - 1;
const float b = 2 * O.y - 1;
if (fabs(a) > fabs(b)) // use squares instead of absolute values
{
r = a;
phi = (Pi / 4.0f) * (b / (a + 1e-6f));
}
else
{
r = b;
phi = (Pi / 2.0f) - (Pi / 4.0f) * (a / (b + 1e-6f));
}
float rr = r * powf(ngon_rad(phi, blades), normalizedStops);
rr = fabs(rr) * (rr > 0 ? 1.0f : -1.0f);
return Vec2(rr * cosf(phi + normalizedStops), rr * sinf(phi + normalizedStops));
}
}
void CDepthOfField::UpdateParameters()
{
bool bOverrideActive = IsActive();
bool bUseGameSettings = (m_pUserActive->GetParam()) ? true : false;
float frameTime = clamp_tpl<float>(gEnv->pTimer->GetFrameTime() * 3.0f, 0.0f, 1.0f);
{
float fUserFocusRange = m_pUserFocusRange->GetParam();
float fUserFocusDistance = m_pUserFocusDistance->GetParam();
float fUserBlurAmount = m_pUserBlurAmount->GetParam();
if (bOverrideActive)
{
fUserFocusRange = fUserFocusDistance = fUserBlurAmount = 0.0f;
}
m_fUserFocusRangeCurr += (fUserFocusRange - m_fUserFocusRangeCurr) * frameTime;
m_fUserFocusDistanceCurr += (fUserFocusDistance - m_fUserFocusDistanceCurr) * frameTime;
m_fUserBlurAmountCurr += (fUserBlurAmount - m_fUserBlurAmountCurr) * frameTime;
}
float focalDistance = 0.0f;
float focalRange = 0.0f;
float blurAmount = 0.0f;
/// Override mode: full control over focal distance / range through parameters.
if (bOverrideActive)
{
focalDistance = m_pFocusDistance->GetParam();
focalRange = m_pFocusRange->GetParam();
blurAmount = m_pBlurAmount->GetParam();
}
/// Blend of TOD settings with "user adjustments". Used by flowgraph / trackview.
else if (bUseGameSettings)
{
m_todFocusRange += (m_fUserFocusRangeCurr - m_todFocusRange) * frameTime;
m_todBlurAmount += (m_fUserBlurAmountCurr - m_todBlurAmount) * frameTime;
focalDistance = m_fUserFocusDistanceCurr;
focalRange = m_fUserFocusRangeCurr;
blurAmount = m_fUserBlurAmountCurr;
}
/// Full TOD control.
else
{
float fTodFocusRange = (CRenderer::CV_r_dof == 2) ? m_pTimeOfDayFocusRange->GetParam() : 0;
float fTodBlurAmount = (CRenderer::CV_r_dof == 2) ? m_pTimeOfDayBlurAmount->GetParam() : 0;
m_todFocusRange += (fTodFocusRange * 2.0f - m_todFocusRange) * frameTime;
m_todBlurAmount += (fTodBlurAmount - m_todBlurAmount) * frameTime;
focalDistance = 0.0f;
focalRange = m_todFocusRange;
blurAmount = m_todBlurAmount;
}
float focalMinDistance = -focalRange * 0.5f;
float focalMaxDistance = focalRange * 0.5f;
Vec4 focusParams;
focusParams.x = 1.0f / (focalMaxDistance + 1e-6f);
focusParams.y = -focalDistance / (focalMaxDistance + 1e-6f);
focusParams.z = 1.0f / (focalMinDistance + 1e-6f);
focusParams.w = -focalDistance / (focalMinDistance + 1e-6f);
// Arbitrary scale added for compatibility with deprecated scatter depth of field. Should get removed
// but will break existing content.
blurAmount *= 2.0f;
m_Parameters.m_FocusParams0 = focusParams;
m_Parameters.m_FocusParams1 = Vec4(CRenderer::CV_r_dofMinZ + m_pFocusMinZ->GetParam(), CRenderer::CV_r_dofMinZScale + m_pFocusMinZScale->GetParam(), 0.0f, blurAmount);
m_Parameters.m_bEnabled = blurAmount > 0.001f;
}
void DepthOfFieldPass::UpdatePassConstants(const DepthOfFieldParameters& dofParams)
{
m_PassConstantBuffer->m_focusParams0 = dofParams.m_FocusParams0;
m_PassConstantBuffer->m_focusParams1 = dofParams.m_FocusParams1;
m_PassConstantBuffer.CopyToDevice();
AzRHI::ConstantBuffer* constantBuffer = m_PassConstantBuffer.GetDeviceConstantBuffer().get();
gcpRendD3D->m_DevMan.BindConstantBuffer(eHWSC_Pixel, constantBuffer, eConstantBufferShaderSlot_PerPass);
}
void DepthOfFieldPass::UpdateGatherSubPassConstants(AZ::u32 targetWidth, AZ::u32 targetHeight, AZ::u32 squareTapCount)
{
const float fFNumber = 8;
const float fNumApertureSides = 8;
const float recipTapCount = 1.0f / ((float)squareTapCount - 1.0f);
for (AZ::u32 y = 0; y < squareTapCount; ++y)
{
for (AZ::u32 x = 0; x < squareTapCount; ++x)
{
Vec2 t = Vec2(x * recipTapCount, y * recipTapCount);
Vec2 result = ToUnitDisk(t, fNumApertureSides, fFNumber);
m_GatherSubPassConstantBuffer->m_taps[x + y * squareTapCount] = Vec4(result.x, result.y, 0, 0);
}
}
m_GatherSubPassConstantBuffer->m_ScreenSize = Vec4(
(float)targetWidth,
(float)targetHeight,
1.0f / (float)targetWidth,
1.0f / (float)targetHeight);
m_GatherSubPassConstantBuffer->m_tapCount = Vec4((float)(squareTapCount * squareTapCount), 0, 0, 0);
m_GatherSubPassConstantBuffer.CopyToDevice();
AzRHI::ConstantBuffer* constantBuffer = m_GatherSubPassConstantBuffer.GetDeviceConstantBuffer().get();
gcpRendD3D->m_DevMan.BindConstantBuffer(eHWSC_Pixel, constantBuffer, eConstantBufferShaderSlot_PerSubPass);
}
void DepthOfFieldPass::UpdateMinCoCSubPassConstants(AZ::u32 targetWidth, AZ::u32 targetHeight)
{
m_MinCoCSubPassConstantBuffer->m_ScreenSize = Vec4(
(float)targetWidth,
(float)targetHeight,
1.0f / (float)targetWidth,
1.0f / (float)targetHeight);
m_MinCoCSubPassConstantBuffer.CopyToDevice();
AzRHI::ConstantBuffer* constantBuffer = m_MinCoCSubPassConstantBuffer.GetDeviceConstantBuffer().get();
gcpRendD3D->m_DevMan.BindConstantBuffer(eHWSC_Pixel, constantBuffer, eConstantBufferShaderSlot_PerSubPass);
}
void DepthOfFieldPass::Init()
{
m_PassConstantBuffer.CreateDeviceBuffer();
m_GatherSubPassConstantBuffer.CreateDeviceBuffer();
m_MinCoCSubPassConstantBuffer.CreateDeviceBuffer();
}
void DepthOfFieldPass::Shutdown()
{
}
void DepthOfFieldPass::Reset()
{
}
void DepthOfFieldPass::Execute()
{
PROFILE_SHADER_SCOPE;
PROFILE_LABEL_SCOPE("DOF");
CDepthOfField* depthOfField = (CDepthOfField*)PostEffectMgr()->GetEffect(ePFX_eDepthOfField);
const DepthOfFieldParameters& dofParams = depthOfField->GetParameters();
if (!dofParams.m_bEnabled)
{
return;
}
UpdatePassConstants(dofParams);
gRenDev->m_cEF.mfRefreshSystemShader("DepthOfField", CShaderMan::s_shPostDepthOfField);
uint64 nSaveFlagsShader_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]);
CTexture* cocCurrent = SPostEffectsUtils::GetCoCCurrentTarget();
if (CRenderer::CV_r_AntialiasingMode == eAT_TAA)
{
gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE2];
}
gcpRendD3D->FX_SetState(GS_NODEPTHTEST);
gcpRendD3D->SetCullMode(R_CULL_NONE);
// For better blending later.
// We skip this on mobile as to reduce memory bandwidth and fetch from the RT instead using GMEM
bool sampleSceneFromRenderTarget = gcpRendD3D->FX_GetEnabledGmemPath(nullptr) && RenderCapabilities::GetFrameBufferFetchCapabilities().test(RenderCapabilities::FBF_COLOR0);
if (!sampleSceneFromRenderTarget)
{
GetUtils().StretchRect(CTexture::s_ptexHDRTarget, CTexture::s_ptexSceneTarget);
}
CTexture* nearFarLayersTemp[2] = { CTexture::s_ptexHDRTargetScaledTmp[0], CTexture::s_ptexHDRTargetScaledTempRT[0] };
assert(nearFarLayersTemp[0]->GetWidth() == CTexture::s_ptexHDRDofLayers[0]->GetWidth() && nearFarLayersTemp[0]->GetHeight() == CTexture::s_ptexHDRDofLayers[0]->GetHeight());
assert(nearFarLayersTemp[1]->GetWidth() == CTexture::s_ptexHDRDofLayers[1]->GetWidth() && nearFarLayersTemp[1]->GetHeight() == CTexture::s_ptexHDRDofLayers[1]->GetHeight());
assert(nearFarLayersTemp[0]->GetPixelFormat() == CTexture::s_ptexHDRDofLayers[0]->GetPixelFormat() && nearFarLayersTemp[1]->GetPixelFormat() == CTexture::s_ptexHDRDofLayers[1]->GetPixelFormat());
{
// 1st downscale stage
{
PROFILE_LABEL_SCOPE("DOWNSCALE LAYERS");
gcpRendD3D->FX_PushRenderTarget(0, CTexture::s_ptexHDRDofLayers[0], NULL); // near
gcpRendD3D->FX_PushRenderTarget(1, CTexture::s_ptexHDRDofLayers[1], NULL); // far
gcpRendD3D->FX_PushRenderTarget(2, CTexture::s_ptexSceneCoC[0], NULL); // CoC near/far
gcpRendD3D->FX_SetColorDontCareActions(0, true, false);
gcpRendD3D->FX_SetColorDontCareActions(1, true, false);
gcpRendD3D->FX_SetColorDontCareActions(2, true, false);
static CCryNameTSCRC techNameDownscaleDof("DownscaleDof");
GetUtils().ShBeginPass(CShaderMan::s_shPostDepthOfField, techNameDownscaleDof, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
GetUtils().SetTexture(CTexture::s_ptexZTarget, 0, FILTER_POINT);
GetUtils().SetTexture(CTexture::s_ptexHDRTarget, 1, FILTER_LINEAR);
GetUtils().SetTexture(cocCurrent, 2, FILTER_POINT);
SD3DPostEffectsUtils::DrawFullScreenTriWPOS(CTexture::s_ptexHDRDofLayers[0]->GetWidth(), CTexture::s_ptexHDRDofLayers[0]->GetHeight());
GetUtils().ShEndPass();
gcpRendD3D->FX_PopRenderTarget(0);
gcpRendD3D->FX_PopRenderTarget(1);
gcpRendD3D->FX_PopRenderTarget(2);
// Avoiding false d3d error (due to deferred rt setup, when ping-pong'ing between RTs we can bump into RTs still bound when binding it as a SRV)
gcpRendD3D->FX_SetActiveRenderTargets();
}
// 2nd downscale stage (tile min CoC)
{
PROFILE_LABEL_SCOPE("MIN COC DOWNSCALE");
uint startingDownscaleIter = 1;
for (uint32 i = startingDownscaleIter; i < MIN_DOF_COC_K; i++)
{
uint32 cocArrayLastElement = i - 1;
if (i == startingDownscaleIter)
{
cocArrayLastElement = 0;
}
gcpRendD3D->FX_PushRenderTarget(0, CTexture::s_ptexSceneCoC[i], NULL); // near
gcpRendD3D->FX_SetColorDontCareActions(0, true, false);
static CCryNameTSCRC techNameTileMinCoC("TileMinCoC");
GetUtils().ShBeginPass(CShaderMan::s_shPostDepthOfField, techNameTileMinCoC, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
UpdateMinCoCSubPassConstants(
CTexture::s_ptexSceneCoC[cocArrayLastElement]->GetWidth(),
CTexture::s_ptexSceneCoC[cocArrayLastElement]->GetHeight());
GetUtils().SetTexture(CTexture::s_ptexSceneCoC[cocArrayLastElement], 0, FILTER_LINEAR);
SD3DPostEffectsUtils::DrawFullScreenTriWPOS(CTexture::s_ptexSceneCoC[i]->GetWidth(), CTexture::s_ptexSceneCoC[i]->GetHeight());
GetUtils().ShEndPass();
gcpRendD3D->FX_PopRenderTarget(0);
gcpRendD3D->FX_SetActiveRenderTargets();
}
}
}
{
// 1st gather pass
{
#if defined(AZ_RESTRICTED_PLATFORM)
#include AZ_RESTRICTED_FILE(DepthOfField_cpp)
#endif
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
#else
AZ::u32 squareTapCount = 7;
#endif
if (gcpRendD3D->FX_GetEnabledGmemPath(nullptr))
{
squareTapCount = CRenderer::CV_r_GMEM_DOF_Gather1_Quality;
}
UpdateGatherSubPassConstants(
nearFarLayersTemp[0]->GetWidth(),
nearFarLayersTemp[0]->GetHeight(),
squareTapCount);
PROFILE_LABEL_SCOPE("FAR/NEAR LAYER");
gcpRendD3D->FX_PushRenderTarget(0, nearFarLayersTemp[0], NULL);
gcpRendD3D->FX_PushRenderTarget(1, nearFarLayersTemp[1], NULL);
gcpRendD3D->FX_PushRenderTarget(2, CTexture::s_ptexSceneCoCTemp, NULL);
gcpRendD3D->FX_SetColorDontCareActions(0, true, false);
gcpRendD3D->FX_SetColorDontCareActions(1, true, false);
gcpRendD3D->FX_SetColorDontCareActions(2, true, false);
gRenDev->m_RP.m_FlagsShader_RT &= ~g_HWSR_MaskBit[HWSR_SAMPLE0];
static CCryNameTSCRC techNameDOF("Dof");
GetUtils().ShBeginPass(CShaderMan::s_shPostDepthOfField, techNameDOF, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
GetUtils().SetTexture(CTexture::s_ptexZTargetScaled, 0, FILTER_POINT);
GetUtils().SetTexture(CTexture::s_ptexHDRDofLayers[0], 1, FILTER_LINEAR);
GetUtils().SetTexture(CTexture::s_ptexHDRDofLayers[1], 2, FILTER_LINEAR);
GetUtils().SetTexture(CTexture::s_ptexSceneCoC[0], 3, FILTER_LINEAR);
GetUtils().SetTexture(CTexture::s_ptexSceneCoC[MIN_DOF_COC_K - 1], 4, FILTER_POINT);
SD3DPostEffectsUtils::DrawFullScreenTriWPOS(nearFarLayersTemp[0]->GetWidth(), nearFarLayersTemp[0]->GetHeight());
GetUtils().ShEndPass();
gcpRendD3D->FX_PopRenderTarget(2);
gcpRendD3D->FX_PopRenderTarget(1);
gcpRendD3D->FX_PopRenderTarget(0);
gcpRendD3D->FX_SetActiveRenderTargets();
}
// 2nd gather iteration
{
AZ::u32 squareTapCount = 3;
if (gcpRendD3D->FX_GetEnabledGmemPath(nullptr))
{
squareTapCount = CRenderer::CV_r_GMEM_DOF_Gather2_Quality;
}
UpdateGatherSubPassConstants(
CTexture::s_ptexHDRDofLayers[0]->GetWidth(),
CTexture::s_ptexHDRDofLayers[0]->GetHeight(),
squareTapCount);
PROFILE_LABEL_SCOPE("FAR/NEAR LAYER ITERATION");
gcpRendD3D->FX_PushRenderTarget(0, CTexture::s_ptexHDRDofLayers[0], NULL);
gcpRendD3D->FX_PushRenderTarget(1, CTexture::s_ptexHDRDofLayers[1], NULL);
gcpRendD3D->FX_SetColorDontCareActions(0, true, false);
gcpRendD3D->FX_SetColorDontCareActions(1, true, false);
gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE0];
static CCryNameTSCRC techNameDOF("Dof");
GetUtils().ShBeginPass(CShaderMan::s_shPostDepthOfField, techNameDOF, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
GetUtils().SetTexture(nearFarLayersTemp[0], 1, FILTER_LINEAR);
GetUtils().SetTexture(nearFarLayersTemp[1], 2, FILTER_LINEAR);
GetUtils().SetTexture(CTexture::s_ptexSceneCoCTemp, 3, FILTER_POINT);
GetUtils().SetTexture(CTexture::s_ptexSceneCoC[MIN_DOF_COC_K - 1], 4, FILTER_POINT);
SD3DPostEffectsUtils::DrawFullScreenTriWPOS(CTexture::s_ptexHDRDofLayers[0]->GetWidth(), CTexture::s_ptexHDRDofLayers[0]->GetHeight());
GetUtils().ShEndPass();
gcpRendD3D->FX_PopRenderTarget(1);
gcpRendD3D->FX_PopRenderTarget(0);
gcpRendD3D->FX_SetActiveRenderTargets();
}
// Final composition
{
PROFILE_LABEL_SCOPE("COMPOSITE");
gcpRendD3D->FX_PushRenderTarget(0, CTexture::s_ptexHDRTarget, NULL);
static CCryNameTSCRC techNameCompositeDof("CompositeDof");
GetUtils().ShBeginPass(CShaderMan::s_shPostDepthOfField, techNameCompositeDof, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
GetUtils().SetTexture(CTexture::s_ptexZTarget, 0, FILTER_POINT);
GetUtils().SetTexture(CTexture::s_ptexHDRDofLayers[0], 1, FILTER_LINEAR);
GetUtils().SetTexture(CTexture::s_ptexHDRDofLayers[1], 2, FILTER_LINEAR);
GetUtils().SetTexture(CTextureManager::Instance()->GetNoTexture(), 3, FILTER_LINEAR);
if (!sampleSceneFromRenderTarget)
{
GetUtils().SetTexture(CTexture::s_ptexSceneTarget, 4, FILTER_POINT);
}
GetUtils().SetTexture(cocCurrent, 5, FILTER_POINT);
SD3DPostEffectsUtils::DrawFullScreenTriWPOS(CTexture::s_ptexHDRTarget->GetWidth(), CTexture::s_ptexHDRTarget->GetHeight());
GetUtils().ShEndPass();
gcpRendD3D->FX_PopRenderTarget(0);
}
CTexture::s_ptexHDRTarget->SetResolved(true);
gcpRendD3D->FX_SetActiveRenderTargets();
}
gRenDev->m_RP.m_FlagsShader_RT = nSaveFlagsShader_RT;
}