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/D3DPostProcess.cpp

1706 lines
64 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.
// Description : Direct3D specific post processing special effects
#include "RenderDll_precompiled.h"
#include "DriverD3D.h"
#include "I3DEngine.h"
#include "D3DPostProcess.h"
#include <Common/RenderCapabilities.h>
#include <Common/Textures/TextureManager.h>
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
#if defined(AZ_RESTRICTED_PLATFORM)
#undef AZ_RESTRICTED_SECTION
#define D3DPOSTPROCESS_CPP_SECTION_1 1
#define D3DPOSTPROCESS_CPP_SECTION_2 2
#endif
enum COLORSPACES
{
CS_sRGB0 = 0, //Most accurate sRGB curve
CS_sRGB1 = 1, //Cheap approximation - pow(col, 1/2.2)
CS_sRGB2 = 2, //cheaper approx - sqrt(col)
CS_P3D65 = 3,
CS_Rec709 = 4,
CS_Rec2020 = 5
};
/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
SDepthTexture* SD3DPostEffectsUtils::GetDepthSurface(CTexture* pTex)
{
if (pTex->GetFlags() & FT_USAGE_MSAA && gRenDev->m_RP.m_MSAAData.Type)
{
return &gcpRendD3D->m_DepthBufferOrigMSAA;
}
return &gcpRendD3D->m_DepthBufferOrig;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
void SD3DPostEffectsUtils::ResolveRT(CTexture*& pDst, const RECT* pSrcRect)
{
AZ_Assert(pDst, "Null texture passed in");
if (!pDst)
{
return;
}
int iTempX, iTempY, iWidth, iHeight;
gcpRendD3D->GetViewport(&iTempX, &iTempY, &iWidth, &iHeight);
CDeviceTexture* pDstResource = pDst->GetDevTexture();
ID3D11RenderTargetView* pOrigRT = gcpRendD3D->m_pNewTarget[0]->m_pTarget;
if (pOrigRT && pDstResource)
{
D3D11_BOX box;
ZeroStruct(box);
if (pSrcRect)
{
box.left = pSrcRect->left;
box.right = pSrcRect->right;
box.top = pSrcRect->top;
box.bottom = pSrcRect->bottom;
}
else
{
box.right = min(pDst->GetWidth(), gcpRendD3D->m_pNewTarget[0]->m_Width);
box.bottom = min(pDst->GetHeight(), gcpRendD3D->m_pNewTarget[0]->m_Height);
}
box.back = 1;
#if defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION D3DPOSTPROCESS_CPP_SECTION_1
#include AZ_RESTRICTED_FILE(D3DPostProcess_cpp)
#endif
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
#else
ID3D11Resource* pSrcResource;
pOrigRT->GetResource(&pSrcResource);
#endif
HRESULT hr = 0;
gcpRendD3D->m_RP.m_PS[gcpRendD3D->m_RP.m_nProcessThreadID].m_RTCopied++;
gcpRendD3D->m_RP.m_PS[gcpRendD3D->m_RP.m_nProcessThreadID].m_RTCopiedSize += pDst->GetDeviceDataSize();
gcpRendD3D->GetDeviceContext().CopySubresourceRegion(pDstResource->Get2DTexture(), 0, 0, 0, 0, pSrcResource, 0, &box);
SAFE_RELEASE(pSrcResource);
}
}
void SD3DPostEffectsUtils::SetSRGBShaderFlags()
{
gRenDev->m_RP.m_FlagsShader_RT &= ~(g_HWSR_MaskBit[HWSR_SRGB0] | g_HWSR_MaskBit[HWSR_SRGB1] | g_HWSR_MaskBit[HWSR_SRGB2]);
switch(CRenderer::CV_r_ColorSpace)
{
case COLORSPACES::CS_sRGB0:
{
gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SRGB0];
break;
}
case COLORSPACES::CS_sRGB1:
{
gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SRGB1];
break;
}
case COLORSPACES::CS_sRGB2:
{
gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SRGB2];
break;
}
case COLORSPACES::CS_P3D65:
{
//todo: Needs support
}
case COLORSPACES::CS_Rec709:
{
//todo: Add support for Rec709 related shader flags to allow control over color space conversion from c++ code
}
case COLORSPACES::CS_Rec2020:
{
//todo: Add support for Rec2020 related shader flags to allow control over color space conversion from c++ code
}
default:
{
CryWarning(VALIDATOR_MODULE_RENDERER, VALIDATOR_WARNING, "Color space not supported");
}
}
}
void SD3DPostEffectsUtils::CopyTextureToScreen(CTexture*& pSrc, const RECT* pSrcRegion, const int filterMode, bool sRGBLookup)
{
CRenderer* rd = gRenDev;
uint64 saveFlagsShader_RT = rd->m_RP.m_FlagsShader_RT;
rd->m_RP.m_FlagsShader_RT &= ~g_HWSR_MaskBit[HWSR_SAMPLE3];
if (sRGBLookup && !pSrc->IsSRGB() && !RenderCapabilities::SupportsTextureViews())
{
//Force SRGB conversion in the shader because the platform doesn't support texture views
rd->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE3];
SetSRGBShaderFlags();
}
static CCryNameTSCRC pRestoreTechName("TextureToTexture");
PostProcessUtils().ShBeginPass(CShaderMan::s_shPostEffects, pRestoreTechName, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
gRenDev->FX_SetState(GS_NODEPTHTEST);
PostProcessUtils().SetTexture(pSrc, 0, filterMode >= 0 ? filterMode : FILTER_POINT, 1, sRGBLookup);
PostProcessUtils().DrawFullScreenTri(pSrc->GetWidth(), pSrc->GetHeight(), 0, pSrcRegion);
PostProcessUtils().ShEndPass();
rd->m_RP.m_FlagsShader_RT = saveFlagsShader_RT;
}
void SD3DPostEffectsUtils::CopyScreenToTexture(CTexture*& pDst, const RECT* pSrcRegion)
{
int iTempX, iTempY, iWidth, iHeight;
gcpRendD3D->GetViewport(&iTempX, &iTempY, &iWidth, &iHeight);
CTexture* source = gcpRendD3D->FX_GetCurrentRenderTarget(0);
if (source)
{
if (source->GetDstFormat() == pDst->GetDstFormat())
{
ResolveRT(pDst, pSrcRegion);
}
else
{
StretchRect(source, pDst, false, false, false, false, SPostEffectsUtils::eDepthDownsample_None, false, pSrcRegion);
}
}
else
{
CDeviceTexture* dstResource = pDst->GetDevTexture();
ID3D11RenderTargetView* sourceRT = gcpRendD3D->FX_GetCurrentRenderTargetSurface(0);
if (sourceRT)
{
D3D11_RENDER_TARGET_VIEW_DESC backbufferDesc;
sourceRT->GetDesc(&backbufferDesc);
const D3DFormat dstFmt = CTexture::DeviceFormatFromTexFormat(pDst->GetDstFormat());
const D3DFormat srcFmt = backbufferDesc.Format;
if (dstFmt == srcFmt)
{
ID3D11Resource* srcResource;
sourceRT->GetResource(&srcResource);
ID3D11Texture2D* srcTex2D = static_cast<ID3D11Texture2D*>(srcResource);
D3D11_TEXTURE2D_DESC srcTex2desc;
srcTex2D->GetDesc(&srcTex2desc);
if (pSrcRegion)
{
D3D11_BOX box = { 0 };
box.left = pSrcRegion->left;
box.right = pSrcRegion->right;
box.top = pSrcRegion->top;
box.bottom = pSrcRegion->bottom;
box.front = 0;
box.back = 1;
gcpRendD3D->GetDeviceContext().CopySubresourceRegion(dstResource->Get2DTexture(), 0, 0, 0, 0, srcResource, 0, &box);
}
else
{
gcpRendD3D->GetDeviceContext().CopySubresourceRegion(dstResource->Get2DTexture(), 0, 0, 0, 0, srcResource, 0, NULL);
}
}
else
{
AZ_Assert(false, "Pixel formats differ");
}
}
else
{
AZ_Assert(false, "No source texture present");
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
void SD3DPostEffectsUtils::StretchRect(CTexture* pSrc, CTexture*& pDst, bool bClearAlpha, bool bDecodeSrcRGBK, bool bEncodeDstRGBK, bool bBigDownsample, EDepthDownsample depthDownsample, bool bBindMultisampled, const RECT* srcRegion)
{
if (!pSrc || !pDst)
{
return;
}
PROFILE_LABEL_SCOPE("STRETCHRECT");
PROFILE_SHADER_SCOPE;
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] | g_HWSR_MaskBit[HWSR_SAMPLE3] |
g_HWSR_MaskBit[HWSR_SAMPLE4] | g_HWSR_MaskBit[HWSR_SAMPLE5] | g_HWSR_MaskBit[HWSR_REVERSE_DEPTH]);
// Get current viewport
int iTempX, iTempY, iWidth, iHeight;
gRenDev->GetViewport(&iTempX, &iTempY, &iWidth, &iHeight);
bool bResample = 0;
if (pSrc->GetWidth() != pDst->GetWidth() || pSrc->GetHeight() != pDst->GetHeight())
{
bResample = 1;
}
const D3DFormat dstFmt = CTexture::DeviceFormatFromTexFormat(pDst->GetDstFormat());
const D3DFormat srcFmt = CTexture::DeviceFormatFromTexFormat(pSrc->GetDstFormat());
bool destinationBaseTextureExists = pDst->GetDevTexture() && pDst->GetDevTexture()->GetBaseTexture();
AZ_Error("Rendering", destinationBaseTextureExists, "'%s' used as destination texture in call to SD3DPostProcessUtils::StretchRect, but it does not have a valid device texture.", pDst->GetName());
bool sourceBaseTextureExists = pSrc->GetDevTexture() && pSrc->GetDevTexture()->GetBaseTexture();
AZ_Error("Rendering", sourceBaseTextureExists, "'%s' used as source texture in call to SD3DPostProcessUtils::StretchRect, but it does not have a valid device texture.", pSrc->GetName());
if (bResample == false && gRenDev->m_RP.m_FlagsShader_RT == 0 && dstFmt == srcFmt)
{
if (sourceBaseTextureExists && destinationBaseTextureExists)
{
gcpRendD3D->GetDeviceContext().CopyResource(pDst->GetDevTexture()->GetBaseTexture(), pSrc->GetDevTexture()->GetBaseTexture());
gRenDev->m_RP.m_FlagsShader_RT = nSaveFlagsShader_RT;
}
return;
}
gcpRendD3D->FX_PushRenderTarget(0, pDst, NULL);
gcpRendD3D->FX_SetColorDontCareActions(0, true, false);
gcpRendD3D->FX_SetDepthDontCareActions(0, true, true);
gcpRendD3D->FX_SetStencilDontCareActions(0, true, true);
gcpRendD3D->FX_SetActiveRenderTargets();
gcpRendD3D->RT_SetViewport(0, 0, pDst->GetWidth(), pDst->GetHeight());
bool bEnableRTSample0 = bBindMultisampled;
if (bEnableRTSample0)
{
gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE0];
}
if (bClearAlpha) // clear alpha to 0
{
gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE1];
}
if (bDecodeSrcRGBK) // decode RGBK src texture
{
gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE2];
}
if (depthDownsample != eDepthDownsample_None) // take minimum/maximum depth from the 4 samples
{
gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE4];
if (depthDownsample == eDepthDownsample_Min)
{
gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_REVERSE_DEPTH];
}
}
if (bEncodeDstRGBK) // encode RGBK dst texture
{
gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE5];
}
static CCryNameTSCRC pTechTexToTex("TextureToTexture");
static CCryNameTSCRC pTechTexToTexResampled("TextureToTextureResampled");
ShBeginPass(CShaderMan::s_shPostEffects, bResample ? pTechTexToTexResampled : pTechTexToTex, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
gRenDev->FX_SetState(GS_NODEPTHTEST);
// Get sample size ratio (based on empirical "best look" approach)
float fSampleSize = ((float)pSrc->GetWidth() / (float)pDst->GetWidth()) * 0.5f;
// Set samples position
//float s1 = fSampleSize / (float) pSrc->GetWidth(); // 2.0 better results on lower res images resizing
//float t1 = fSampleSize / (float) pSrc->GetHeight();
CTexture* pOffsetTex = bBigDownsample ? pDst : pSrc;
float s1 = 0.5f / (float) pOffsetTex->GetWidth(); // 2.0 better results on lower res images resizing
float t1 = 0.5f / (float) pOffsetTex->GetHeight();
Vec4 pParams0, pParams1;
if (bBigDownsample)
{
// Use rotated grid + middle sample (~quincunx)
pParams0 = Vec4(s1 * 0.96f, t1 * 0.25f, -s1 * 0.25f, t1 * 0.96f);
pParams1 = Vec4(-s1 * 0.96f, -t1 * 0.25f, s1 * 0.25f, -t1 * 0.96f);
}
else
{
// Use box filtering (faster - can skip bilinear filtering, only 4 taps)
pParams0 = Vec4(-s1, -t1, s1, -t1);
pParams1 = Vec4(s1, t1, -s1, t1);
}
static CCryNameR pParam0Name("texToTexParams0");
static CCryNameR pParam1Name("texToTexParams1");
CShaderMan::s_shPostEffects->FXSetPSFloat(pParam0Name, &pParams0, 1);
CShaderMan::s_shPostEffects->FXSetPSFloat(pParam1Name, &pParams1, 1);
int nFilter = bResample ? FILTER_LINEAR : FILTER_POINT;
pSrc->Apply(0, CTexture::GetTexState(STexState(nFilter, true)), EFTT_UNKNOWN, -1,
(bBindMultisampled && gRenDev->m_RP.m_MSAAData.Type) ? SResourceView::DefaultViewMS : SResourceView::DefaultView); // bind as msaa target (if valid)
DrawFullScreenTri(pDst->GetWidth(), pDst->GetHeight(), 0, srcRegion);
ShEndPass();
// Restore previous viewport
gcpRendD3D->FX_PopRenderTarget(0);
gcpRendD3D->RT_SetViewport(iTempX, iTempY, iWidth, iHeight);
gRenDev->m_RP.m_FlagsShader_RT = nSaveFlagsShader_RT;
}
void SD3DPostEffectsUtils::SwapRedBlue([[maybe_unused]] CTexture* pSrc, [[maybe_unused]] CTexture* pDst)
{
#if defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION D3DPOSTPROCESS_CPP_SECTION_2
#include AZ_RESTRICTED_FILE(D3DPostProcess_cpp)
#endif
}
void SD3DPostEffectsUtils::DownsampleDepth(CTexture* pSrc, CTexture* pDst, bool bFromSingleChannel)
{
PROFILE_LABEL_SCOPE("DOWNSAMPLE_DEPTH");
PROFILE_SHADER_SCOPE;
if (!pDst)
{
return;
}
CD3D9Renderer* const __restrict rd = gcpRendD3D;
uint64 nSaveFlagsShader_RT = rd->m_RP.m_FlagsShader_RT;
rd->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]);
// Get current viewport
int prevVPX, prevVPY, prevVPWidth, prevVPHeight;
rd->GetViewport(&prevVPX, &prevVPY, &prevVPWidth, &prevVPHeight);
bool bUseDeviceDepth = (pSrc == NULL);
rd->FX_PushRenderTarget(0, pDst, NULL);
// Metal Load/Store Actions
rd->FX_SetColorDontCareActions(0, true, false);
rd->FX_SetDepthDontCareActions(0, true, true);
rd->FX_SetStencilDontCareActions(0, true, true);
int dstWidth = pDst->GetWidth();
int dstHeight = pDst->GetHeight();
rd->RT_SetViewport(0, 0, dstWidth, dstHeight);
if (bUseDeviceDepth)
{
rd->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE0];
}
if (bFromSingleChannel)
{
rd->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE1];
}
static CCryNameTSCRC pTech("DownsampleDepth");
ShBeginPass(CShaderMan::s_shPostEffects, pTech, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
rd->FX_SetState(GS_NODEPTHTEST);
int srcWidth = bUseDeviceDepth ? rd->GetWidth() : pSrc->GetWidth();
int srcHeight = bUseDeviceDepth ? rd->GetHeight() : pSrc->GetHeight();
if (bUseDeviceDepth)
{
rd->m_DevMan.BindSRV(eHWSC_Pixel, &rd->m_pZBufferDepthReadOnlySRV, 0, 1);
}
else
{
SetTexture(pSrc, 0, FILTER_POINT, 1);
}
#if defined(CRY_USE_METAL) || defined(ANDROID)
const Vec2& vDownscaleFactor = gcpRendD3D->m_RP.m_CurDownscaleFactor;
gRenDev->RT_SetScissor(true, 0, 0, pDst->GetWidth() * vDownscaleFactor.x + 0.5f, pDst->GetHeight() * vDownscaleFactor.y + 0.5f);
#endif
if (GetShaderLanguage() == eSL_GLES3_0)
{
// There's a bug in Qualcomm OpenGL ES 3.0 drivers that cause the device
// shader compiler to crash if we use "textureSize" in the shader to get the texture dimensions.
Vec4 texSize = Vec4(srcWidth, srcHeight, 0, 0);
static CCryNameR texSizeParam("DownsampleDepth_DepthTex_Dimensions");
CShaderMan::s_shPostEffects->FXSetPSFloat(texSizeParam, &texSize, 1);
}
RECT source = { 0, 0, pDst->GetWidth(), pDst->GetHeight() };
//Round up to even to handle uneven dimensions
dstWidth = (dstWidth + 1) & ~1;
dstHeight = (dstHeight + 1) & ~1;
DrawFullScreenTri(dstWidth, dstHeight, 0.f, &source);
#if defined(CRY_USE_METAL) || defined(ANDROID)
gRenDev->RT_SetScissor(false, 0, 0, 0, 0);
#endif
ShEndPass();
if (bUseDeviceDepth)
{
D3DShaderResourceView* pNullSRV[1] = { NULL };
rd->m_DevMan.BindSRV(eHWSC_Pixel, pNullSRV, 0, 1);
rd->FX_Commit();
}
// Restore previous viewport
rd->FX_PopRenderTarget(0);
rd->RT_SetViewport(prevVPX, prevVPY, prevVPWidth, prevVPHeight);
rd->m_RP.m_FlagsShader_RT = nSaveFlagsShader_RT;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
void SD3DPostEffectsUtils::DownsampleDepthUsingCompute(CTexture* pSrc, CTexture** pDstArr, bool bFromSingleChannel)
{
PROFILE_LABEL_SCOPE("DOWNSAMPLE_DEPTHCS");
PROFILE_SHADER_SCOPE;
if (!pDstArr || !pDstArr[0])
{
return;
}
CTexture* pDst = pDstArr[0];
CD3D9Renderer* const __restrict rd = gcpRendD3D;
uint64 saveFlagsShader_RT = rd->m_RP.m_FlagsShader_RT;
rd->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]);
bool useDeviceDepth = (pSrc == NULL);
rd->RT_SetViewport(0, 0, pDst->GetWidth(), pDst->GetHeight());
if (useDeviceDepth)
{
rd->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE0];
}
if (bFromSingleChannel)
{
rd->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE1];
}
static CCryNameTSCRC pTech("DownsampleDepthCS");
ShBeginPass(CShaderMan::s_shPostEffects, pTech, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
rd->FX_SetState(GS_NODEPTHTEST);
int srcWidth = useDeviceDepth ? rd->GetWidth() : pSrc->GetWidth();
int srcHeight = useDeviceDepth ? rd->GetHeight() : pSrc->GetHeight();
if (useDeviceDepth)
{
rd->m_DevMan.BindSRV(eHWSC_Compute, &rd->m_pZBufferDepthReadOnlySRV, 0, 1);
}
else
{
D3DShaderResourceView* pShaderResrouce[1] = {
(pSrc->GetShaderResourceView()),
};
rd->m_DevMan.BindSRV(eHWSC_Compute, pShaderResrouce, 0, 1);
}
static CCryNameR paramSrcSize("SrcTexSizeAndCount");
Vec4 vParamSrcSize(srcWidth, srcHeight, 2, 0);
CShaderMan::s_shDeferredShading->FXSetCSFloat(paramSrcSize, &vParamSrcSize, 1);
rd->FX_Commit();
D3DUnorderedAccessView* UAVs[2] = {
(pDstArr[0]->GetDeviceUAV()),
(pDstArr[1]->GetDeviceUAV()),
};
rd->GetDeviceContext().CSSetUnorderedAccessViews(0, 2, UAVs, NULL);
uint32 dispatchSizeX = srcWidth / 8 + (srcWidth % 8 > 0 ? 1 : 0);
uint32 dispatchSizeY = srcHeight / 8 + (srcHeight % 8 > 0 ? 1 : 0);
rd->m_DevMan.Dispatch(dispatchSizeX, dispatchSizeY, 1);
ShEndPass();
rd->m_RP.m_FlagsShader_RT = saveFlagsShader_RT;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
void SD3DPostEffectsUtils::DownsampleUsingCompute(CTexture* pSrc, CTexture** pDstArr)
{
AZ_Assert(pSrc && pDstArr[0], "Null textures passed in");
const int maxIterations = 3;
int numIters = 1;
D3DUnorderedAccessView* pUAV[maxIterations];
pUAV[0] = (pDstArr[0]->GetDeviceUAV());
for (int i = 1; i < maxIterations; i++)
{
if (pDstArr[i])
{
numIters++;
pUAV[i] = (pDstArr[i]->GetDeviceUAV());
}
else
{
// Need to bind a UAV or Metal will complain... even if not written to.
pUAV[i] = pUAV[0];
break;
}
}
PROFILE_LABEL_SCOPE("DOWNSAMPLE_SCENE_CS");
PROFILE_SHADER_SCOPE;
CTexture* pSrcRT = pSrc;
static CCryNameTSCRC pTech("TextureToTextureCS");
ShBeginPass(CShaderMan::s_shPostEffects, pTech, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
int srcWidth = pSrcRT->GetWidth();
int srcHeight = pSrcRT->GetHeight();
CD3D9Renderer* const __restrict rd = gcpRendD3D;
D3DShaderResourceView* pSRV = (pSrcRT->GetShaderResourceView());
rd->m_DevMan.BindSRV(eHWSC_Compute, &pSRV, 0, 1);
static CCryNameR param("gNumIterations");
Vec4 vParam(numIters, 0, 0, 0);
CShaderMan::s_shDeferredShading->FXSetCSFloat(param, &vParam, 1);
rd->FX_Commit();
rd->GetDeviceContext().CSSetUnorderedAccessViews(0, 3, pUAV, NULL);
// Grid dims must match in shader
const uint32 kernelGridX = 8;
const uint32 kernelGridY = 8;
uint32 dispatchSizeX = srcWidth / kernelGridX + (srcWidth % kernelGridX > 0 ? 1 : 0);
uint32 dispatchSizeY = srcHeight / kernelGridY + (srcHeight % kernelGridY > 0 ? 1 : 0);
rd->m_DevMan.Dispatch(dispatchSizeX, dispatchSizeY, 1);
ShEndPass();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
void SD3DPostEffectsUtils::Downsample(CTexture* pSrc, CTexture* pDst, int nSrcW, int nSrcH, int nDstW, int nDstH, EFilterType eFilter, bool bSetTarget)
{
if (!pSrc)
{
return;
}
PROFILE_LABEL_SCOPE("DOWNSAMPLE");
PROFILE_SHADER_SCOPE;
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]);
// Get current viewport
int iTempX, iTempY, iWidth, iHeight;
gRenDev->GetViewport(&iTempX, &iTempY, &iWidth, &iHeight);
if (bSetTarget)
{
gcpRendD3D->FX_PushRenderTarget(0, pDst, NULL);
}
gcpRendD3D->RT_SetViewport(0, 0, nDstW, nDstH);
// Currently only exact multiples supported
Vec2 vSamples(float(nSrcW) / nDstW, float(nSrcH) / nDstH);
const Vec2 vSampleSize(1.f / nSrcW, 1.f / nSrcH);
const Vec2 vPixelSize(1.f / nDstW, 1.f / nDstH);
// Adjust UV space if source rect smaller than texture
const float fClippedRatioX = float(nSrcW) / pSrc->GetWidth();
const float fClippedRatioY = float(nSrcH) / pSrc->GetHeight();
// Base kernel size in pixels
float fBaseKernelSize = 1.f;
// How many lines of border samples to skip
float fBorderSamplesToSkip = 0.f;
switch (eFilter)
{
default:
case FilterType_Box:
fBaseKernelSize = 1.f;
fBorderSamplesToSkip = 0.f;
break;
case FilterType_Tent:
fBaseKernelSize = 2.f;
fBorderSamplesToSkip = 0.f;
gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE0];
break;
case FilterType_Gauss:
// The base kernel for Gaussian filter is 3x3 pixels [-1.5 .. 1.5]
// Samples on the borders are ignored due to small contribution
// so the actual kernel size is N*3 - 2 where N is number of samples per pixel
fBaseKernelSize = 3.f;
fBorderSamplesToSkip = 1.f;
gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE1];
break;
case FilterType_Lanczos:
fBaseKernelSize = 3.f;
fBorderSamplesToSkip = 0.f;
gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE2];
break;
}
// Kernel position step
const Vec2 vSampleStep(1.f / vSamples.x, 1.f / vSamples.y);
// The actual kernel radius in pixels
const Vec2 vKernelRadius = 0.5f * Vec2(fBaseKernelSize, fBaseKernelSize) - fBorderSamplesToSkip * vSampleStep;
// UV offset from pixel center to first (top-left) sample
const Vec2 vFirstSampleOffset(0.5f * vSampleSize.x - vKernelRadius.x * vPixelSize.x,
0.5f * vSampleSize.y - vKernelRadius.y * vPixelSize.y);
// Kernel position of first (top-left) sample
const Vec2 vFirstSamplePos = -vKernelRadius + 0.5f * vSampleStep;
static CCryNameTSCRC pTechName("TextureToTextureResampleFilter");
ShBeginPass(CShaderMan::s_shPostEffects, pTechName, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
gRenDev->FX_SetState(GS_NODEPTHTEST);
const Vec4 pParams0(vKernelRadius.x, vKernelRadius.y, fClippedRatioX, fClippedRatioY);
const Vec4 pParams1(vSampleSize.x, vSampleSize.y, vFirstSampleOffset.x, vFirstSampleOffset.y);
const Vec4 pParams2(vSampleStep.x, vSampleStep.y, vFirstSamplePos.x, vFirstSamplePos.y);
static CCryNameR pParam0Name("texToTexParams0");
static CCryNameR pParam1Name("texToTexParams1");
static CCryNameR pParam2Name("texToTexParams2");
CShaderMan::s_shPostEffects->FXSetPSFloat(pParam0Name, &pParams0, 1);
CShaderMan::s_shPostEffects->FXSetPSFloat(pParam1Name, &pParams1, 1);
CShaderMan::s_shPostEffects->FXSetPSFloat(pParam2Name, &pParams2, 1);
SetTexture(pSrc, 0, FILTER_NONE);
DrawFullScreenTri(nDstW, nDstH);
ShEndPass();
// Restore previous viewport
if (bSetTarget)
{
gcpRendD3D->FX_PopRenderTarget(0);
}
gcpRendD3D->RT_SetViewport(iTempX, iTempY, iWidth, iHeight);
gRenDev->m_RP.m_FlagsShader_RT = nSaveFlagsShader_RT;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
void SD3DPostEffectsUtils::DownsampleStable(CTexture* pSrcRT, CTexture* pDstRT, bool bKillFireflies)
{
PROFILE_LABEL_SCOPE("DOWNSAMPLE_STABLE");
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] | g_HWSR_MaskBit[HWSR_SAMPLE4] | g_HWSR_MaskBit[HWSR_SAMPLE5]);
int iTempX, iTempY, iWidth, iHeight;
gRenDev->GetViewport(&iTempX, &iTempY, &iWidth, &iHeight);
gcpRendD3D->FX_PushRenderTarget(0, pDstRT, NULL);
gcpRendD3D->RT_SetViewport(0, 0, pDstRT->GetWidth(), pDstRT->GetHeight());
gcpRendD3D->FX_SetColorDontCareActions(0, true, false);
gcpRendD3D->FX_SetDepthDontCareActions(0, true, true);
gcpRendD3D->FX_SetStencilDontCareActions(0, true, true);
if (bKillFireflies)
{
gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE0];
}
static CCryNameTSCRC techName("DownsampleStable");
ShBeginPass(CShaderMan::s_shPostEffects, techName, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
gcpRendD3D->FX_SetState(GS_NODEPTHTEST);
pSrcRT->Apply(0, CTexture::GetTexState(STexState(FILTER_LINEAR, true)));
DrawFullScreenTri(pDstRT->GetWidth(), pDstRT->GetHeight());
ShEndPass();
gcpRendD3D->FX_PopRenderTarget(0);
gcpRendD3D->RT_SetViewport(iTempX, iTempY, iWidth, iHeight);
gRenDev->m_RP.m_FlagsShader_RT = nSaveFlagsShader_RT;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
void SD3DPostEffectsUtils::TexBlurIterative(CTexture* pTex, int nIterationsMul, bool bDilate, CTexture* pBlurTmp)
{
if (!pTex)
{
return;
}
SDynTexture* tpBlurTemp = 0;
if (!pBlurTmp)
{
tpBlurTemp = new SDynTexture(pTex->GetWidth(), pTex->GetHeight(), pTex->GetDstFormat(), eTT_2D, FT_STATE_CLAMP | FT_USAGE_RENDERTARGET, "TempBlurRT");
if (tpBlurTemp)
{
tpBlurTemp->Update(pTex->GetWidth(), pTex->GetHeight());
}
if (!tpBlurTemp || !tpBlurTemp->m_pTexture)
{
SAFE_DELETE(tpBlurTemp);
return;
}
}
PROFILE_LABEL_SCOPE("TEXBLUR_16TAPS");
PROFILE_SHADER_SCOPE;
CTexture* pTempRT = pBlurTmp ? pBlurTmp : tpBlurTemp->m_pTexture;
// Get current viewport
int iTempX, iTempY, iWidth, iHeight;
gRenDev->GetViewport(&iTempX, &iTempY, &iWidth, &iHeight);
// Iterative blur (aka Kawase): 4 taps, 16 taps, 64 taps, 256 taps, etc
uint64 nFlagsShaderRT = gRenDev->m_RP.m_FlagsShader_RT;
gRenDev->m_RP.m_FlagsShader_RT &= ~(g_HWSR_MaskBit[HWSR_SAMPLE0] | g_HWSR_MaskBit[HWSR_SAMPLE1]);
//// Dilate - use 2 passes, horizontal+vertical
//if( bDilate )
//{
// gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE0];
// nIterationsMul = 1;
//}
for (int i = 1; i <= nIterationsMul; ++i)
{
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 1st iteration (4 taps)
gcpRendD3D->FX_PushRenderTarget(0, pTempRT, NULL);
gcpRendD3D->FX_SetActiveRenderTargets(false);// Avoiding invalid 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->RT_SetViewport(0, 0, pTex->GetWidth(), pTex->GetHeight());
// only regular gaussian blur supporting masking
static CCryNameTSCRC pTechName("Blur4Taps");
uint32 nPasses;
CShaderMan::s_shPostEffects->FXSetTechnique(pTechName);
CShaderMan::s_shPostEffects->FXBegin(&nPasses, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
gRenDev->FX_SetState(GS_NODEPTHTEST);
// setup texture offsets, for texture sampling
// Get sample size ratio (based on empirical "best look" approach)
float fSampleSize = 1.0f * ((float) i);//((float)pTex->GetWidth()/(float)pTex->GetWidth()) * 0.5f;
// Set samples position
float s1 = fSampleSize / (float) pTex->GetWidth(); // 2.0 better results on lower res images resizing
float t1 = fSampleSize / (float) pTex->GetHeight();
// Use rotated grid
Vec4 pParams0 = Vec4(s1 * 0.96f, t1 * 0.25f, -s1 * 0.25f, t1 * 0.96f);//Vec4(-s1, -t1, s1, -t1);
Vec4 pParams1 = Vec4(-s1 * 0.96f, -t1 * 0.25f, s1 * 0.25f, -t1 * 0.96f);//Vec4(s1, t1, -s1, t1);
static CCryNameR pParam0Name("texToTexParams0");
static CCryNameR pParam1Name("texToTexParams1");
CShaderMan::s_shPostEffects->FXSetPSFloat(pParam0Name, &pParams0, 1);
CShaderMan::s_shPostEffects->FXSetPSFloat(pParam1Name, &pParams1, 1);
CShaderMan::s_shPostEffects->FXBeginPass(0);
SetTexture(pTex, 0, FILTER_LINEAR);
DrawFullScreenTri(pTex->GetWidth(), pTex->GetHeight());
CShaderMan::s_shPostEffects->FXEndPass();
CShaderMan::s_shPostEffects->FXEnd();
gcpRendD3D->FX_PopRenderTarget(0);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 2nd iteration (4 x 4 taps)
if (bDilate)
{
gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE1];
}
gcpRendD3D->FX_PushRenderTarget(0, pTex, NULL);
gcpRendD3D->FX_SetActiveRenderTargets(false);// Avoiding invalid 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->RT_SetViewport(0, 0, pTex->GetWidth(), pTex->GetHeight());
CShaderMan::s_shPostEffects->FXSetTechnique(pTechName);
CShaderMan::s_shPostEffects->FXBegin(&nPasses, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
gRenDev->FX_SetState(GS_NODEPTHTEST);
// increase kernel size for second iteration
fSampleSize = 2.0 * ((float) i);
// Set samples position
s1 = fSampleSize / (float) pTex->GetWidth(); // 2.0 better results on lower res images resizing
t1 = fSampleSize / (float) pTex->GetHeight();
// Use rotated grid
pParams0 = Vec4(s1 * 0.96f, t1 * 0.25f, -s1 * 0.25f, t1 * 0.96f);//Vec4(-s1, -t1, s1, -t1);
pParams1 = Vec4(-s1 * 0.96f, -t1 * 0.25f, s1 * 0.25f, -t1 * 0.96f);//Vec4(s1, t1, -s1, t1);
CShaderMan::s_shPostEffects->FXSetPSFloat(pParam0Name, &pParams0, 1);
CShaderMan::s_shPostEffects->FXSetPSFloat(pParam1Name, &pParams1, 1);
CShaderMan::s_shPostEffects->FXBeginPass(0);
SetTexture(pTempRT, 0, FILTER_LINEAR);
DrawFullScreenTri(pTex->GetWidth(), pTex->GetHeight());
CShaderMan::s_shPostEffects->FXEndPass();
CShaderMan::s_shPostEffects->FXEnd();
gcpRendD3D->FX_PopRenderTarget(0);
}
gRenDev->m_RP.m_FlagsShader_RT = nFlagsShaderRT;
// Restore previous viewport
gcpRendD3D->RT_SetViewport(iTempX, iTempY, iWidth, iHeight);
SAFE_DELETE(tpBlurTemp);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
void SD3DPostEffectsUtils::TexBlurDirectional(CTexture* pTex, const Vec2& vDir, int nIterationsMul, CTexture* pBlurTmp)
{
if (!pTex)
{
return;
}
SDynTexture* tpBlurTemp = 0;
if (!pBlurTmp)
{
tpBlurTemp = new SDynTexture(pTex->GetWidth(), pTex->GetHeight(), pTex->GetDstFormat(), eTT_2D, FT_STATE_CLAMP | FT_USAGE_RENDERTARGET, "TempBlurRT");
if (tpBlurTemp)
{
tpBlurTemp->Update(pTex->GetWidth(), pTex->GetHeight());
}
if (!tpBlurTemp || !tpBlurTemp->m_pTexture)
{
SAFE_DELETE(tpBlurTemp);
return;
}
}
PROFILE_LABEL_SCOPE("TEXBLUR_DIRECTIONAL");
PROFILE_SHADER_SCOPE;
CTexture* pTempRT = pBlurTmp;
if (!pBlurTmp)
{
pTempRT = tpBlurTemp->m_pTexture;
}
static CCryNameTSCRC pTechName("BlurDirectional");
static CCryNameR pParam0Name("texToTexParams0");
static CCryNameR pParam1Name("texToTexParams1");
static CCryNameR pParam2Name("texToTexParams2");
static CCryNameR pParam3Name("texToTexParams3");
// Get current viewport
int iTempX, iTempY, iWidth, iHeight;
gRenDev->GetViewport(&iTempX, &iTempY, &iWidth, &iHeight);
// Iterative directional blur: 1 iter: 8 taps, 64 taps, 2 iter: 512 taps, 4096 taps...
float fSampleScale = 1.0f;
for (int i = nIterationsMul; i >= 1; --i)
{
// 1st iteration (4 taps)
gcpRendD3D->FX_PushRenderTarget(0, pTempRT, NULL);
gcpRendD3D->RT_SetViewport(0, 0, pTex->GetWidth(), pTex->GetHeight());
uint32 nPasses;
CShaderMan::s_shPostEffects->FXSetTechnique(pTechName);
CShaderMan::s_shPostEffects->FXBegin(&nPasses, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
gRenDev->FX_SetState(GS_NODEPTHTEST);
float fSampleSize = fSampleScale;
// Set samples position
float s1 = fSampleSize / (float) pTex->GetWidth(); // 2.0 better results on lower res images resizing
float t1 = fSampleSize / (float) pTex->GetHeight();
Vec2 vBlurDir;
vBlurDir.x = s1 * vDir.x;
vBlurDir.y = t1 * vDir.y;
// Use rotated grid
Vec4 pParams0 = Vec4(-vBlurDir.x * 4.0f, -vBlurDir.y * 4.0f, -vBlurDir.x * 3.0f, -vBlurDir.y * 3.0f);
Vec4 pParams1 = Vec4(-vBlurDir.x * 2.0f, -vBlurDir.y * 2.0f, -vBlurDir.x, -vBlurDir.y);
Vec4 pParams2 = Vec4(vBlurDir.x * 2.0f, vBlurDir.y * 2.0f, vBlurDir.x, vBlurDir.y);
Vec4 pParams3 = Vec4(vBlurDir.x * 4.0f, vBlurDir.y * 4.0f, vBlurDir.x * 3.0f, vBlurDir.y * 3.0f);
CShaderMan::s_shPostEffects->FXSetPSFloat(pParam0Name, &pParams0, 1);
CShaderMan::s_shPostEffects->FXSetPSFloat(pParam1Name, &pParams1, 1);
CShaderMan::s_shPostEffects->FXSetPSFloat(pParam2Name, &pParams2, 1);
CShaderMan::s_shPostEffects->FXSetPSFloat(pParam3Name, &pParams3, 1);
CShaderMan::s_shPostEffects->FXBeginPass(0);
SetTexture(pTex, 0, FILTER_LINEAR, TADDR_BORDER);
SetTexture(CTextureManager::Instance()->GetDefaultTexture("ScreenNoiseMap"), 1, FILTER_POINT, 0);
DrawFullScreenTri(pTex->GetWidth(), pTex->GetHeight());
CShaderMan::s_shPostEffects->FXEndPass();
CShaderMan::s_shPostEffects->FXEnd();
gcpRendD3D->FX_PopRenderTarget(0);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 2nd iteration (4 x 4 taps)
gcpRendD3D->FX_PushRenderTarget(0, pTex, NULL);
gcpRendD3D->RT_SetViewport(0, 0, pTex->GetWidth(), pTex->GetHeight());
CShaderMan::s_shPostEffects->FXSetTechnique(pTechName);
CShaderMan::s_shPostEffects->FXBegin(&nPasses, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
gRenDev->FX_SetState(GS_NODEPTHTEST);
fSampleScale *= 0.75f;
fSampleSize = fSampleScale;
s1 = fSampleSize / (float) pTex->GetWidth(); // 2.0 better results on lower res images resizing
t1 = fSampleSize / (float) pTex->GetHeight();
vBlurDir.x = vDir.x * s1;
vBlurDir.y = vDir.y * t1;
pParams0 = Vec4(-vBlurDir.x * 4.0f, -vBlurDir.y * 4.0f, -vBlurDir.x * 3.0f, -vBlurDir.y * 3.0f);
pParams1 = Vec4(-vBlurDir.x * 2.0f, -vBlurDir.y * 2.0f, -vBlurDir.x, -vBlurDir.y);
pParams2 = Vec4(vBlurDir.x, vBlurDir.y, vBlurDir.x * 2.0f, vBlurDir.y * 2.0f);
pParams3 = Vec4(vBlurDir.x * 3.0f, vBlurDir.y * 3.0f, vBlurDir.x * 4.0f, vBlurDir.y * 4.0f);
CShaderMan::s_shPostEffects->FXSetPSFloat(pParam0Name, &pParams0, 1);
CShaderMan::s_shPostEffects->FXSetPSFloat(pParam1Name, &pParams1, 1);
CShaderMan::s_shPostEffects->FXSetPSFloat(pParam2Name, &pParams2, 1);
CShaderMan::s_shPostEffects->FXSetPSFloat(pParam3Name, &pParams3, 1);
CShaderMan::s_shPostEffects->FXBeginPass(0);
SetTexture(pTempRT, 0, FILTER_LINEAR, TADDR_BORDER);
SetTexture(CTextureManager::Instance()->GetDefaultTexture("ScreenNoiseMap"), 1, FILTER_POINT, 0);
DrawFullScreenTri(pTex->GetWidth(), pTex->GetHeight());
CShaderMan::s_shPostEffects->FXEndPass();
CShaderMan::s_shPostEffects->FXEnd();
gcpRendD3D->FX_PopRenderTarget(0);
fSampleScale *= 0.5f;
}
// Restore previous viewport
gcpRendD3D->RT_SetViewport(iTempX, iTempY, iWidth, iHeight);
SAFE_DELETE(tpBlurTemp);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
void SD3DPostEffectsUtils::TexBlurGaussian(CTexture* pTex, [[maybe_unused]] int nAmount, float fScale, float fDistribution, bool bAlphaOnly, CTexture* pMask, bool bSRGB, CTexture* pBlurTmp)
{
if (!pTex)
{
return;
}
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]);
PROFILE_LABEL_SCOPE("TEXBLUR_GAUSSIAN");
if (bSRGB)
{
gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE0];
}
CTexture* pTempRT = pBlurTmp;
SDynTexture* tpBlurTemp = 0;
if (!pBlurTmp)
{
tpBlurTemp = new SDynTexture(pTex->GetWidth(), pTex->GetHeight(), pTex->GetDstFormat(), eTT_2D, FT_STATE_CLAMP | FT_USAGE_RENDERTARGET, "TempBlurRT");
if (tpBlurTemp)
{
tpBlurTemp->Update(pTex->GetWidth(), pTex->GetHeight());
}
if (!tpBlurTemp || !tpBlurTemp->m_pTexture)
{
SAFE_DELETE(tpBlurTemp);
return;
}
pTempRT = tpBlurTemp->m_pTexture;
}
PROFILE_SHADER_SCOPE;
// Get current viewport
int iTempX, iTempY, iWidth, iHeight;
gRenDev->GetViewport(&iTempX, &iTempY, &iWidth, &iHeight);
gcpRendD3D->RT_SetViewport(0, 0, pTex->GetWidth(), pTex->GetHeight());
Vec4 vWhite(1.0f, 1.0f, 1.0f, 1.0f);
// TODO: Make test with Martin's idea about the horizontal blur pass with vertical offset.
// only regular gaussian blur supporting masking
static CCryNameTSCRC pTechName("GaussBlurBilinear");
static CCryNameTSCRC pTechNameMasked("MaskedGaussBlurBilinear");
static CCryNameTSCRC pTechName1("GaussAlphaBlur");
//ShBeginPass(CShaderMan::m_shPostEffects, , FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
uint32 nPasses;
CShaderMan::s_shPostEffects->FXSetTechnique((!bAlphaOnly) ? ((pMask) ? pTechNameMasked : pTechName) : pTechName1);
CShaderMan::s_shPostEffects->FXBegin(&nPasses, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
gRenDev->FX_SetState(GS_NODEPTHTEST);
// setup texture offsets, for texture sampling
float s1 = 1.0f / (float) pTex->GetWidth();
float t1 = 1.0f / (float) pTex->GetHeight();
// Horizontal/Vertical pass params
const int nSamples = 16;
int nHalfSamples = (nSamples >> 1);
Vec4 pHParams[32], pVParams[32], pWeightsPS[32];
float pWeights[32], fWeightSum = 0;
memset(pWeights, 0, sizeof(pWeights));
int s;
for (s = 0; s < nSamples; ++s)
{
if (fDistribution != 0.0f)
{
pWeights[s] = GaussianDistribution1D(s - nHalfSamples, fDistribution);
}
else
{
pWeights[s] = 0.0f;
}
fWeightSum += pWeights[s];
}
// normalize weights
for (s = 0; s < nSamples; ++s)
{
pWeights[s] /= fWeightSum;
}
// set bilinear offsets
for (s = 0; s < nHalfSamples; ++s)
{
float off_a = pWeights[s * 2];
float off_b = ((s * 2 + 1) <= nSamples - 1) ? pWeights[s * 2 + 1] : 0;
float a_plus_b = (off_a + off_b);
if (a_plus_b == 0)
{
a_plus_b = 1.0f;
}
float offset = off_b / a_plus_b;
pWeights[s] = off_a + off_b;
pWeights[s] *= fScale;
pWeightsPS[s] = vWhite * pWeights[s];
float fCurrOffset = (float) s * 2 + offset - nHalfSamples;
pHParams[s] = Vec4(s1 * fCurrOffset, 0, 0, 0);
pVParams[s] = Vec4(0, t1 * fCurrOffset, 0, 0);
}
STexState sTexState = STexState(FILTER_LINEAR, true);
static CCryNameR clampTCName("clampTC");
static CCryNameR pParam0Name("psWeights");
static CCryNameR pParam1Name("PI_psOffsets");
Vec4 clampTC(0.0f, 1.0f, 0.0f, 1.0f);
if (pTex->GetWidth() == gcpRendD3D->GetWidth() && pTex->GetHeight() == gcpRendD3D->GetHeight())
{
// clamp manually in shader since texture clamp won't apply for smaller viewport
clampTC = Vec4(0.0f, gRenDev->m_RP.m_CurDownscaleFactor.x, 0.0f, gRenDev->m_RP.m_CurDownscaleFactor.y);
}
//SetTexture(pTex, 0, FILTER_LINEAR);
CShaderMan::s_shPostEffects->FXSetPSFloat(clampTCName, &clampTC, 1);
CShaderMan::s_shPostEffects->FXSetPSFloat(pParam0Name, pWeightsPS, nHalfSamples);
//for(int p(1); p<= nAmount; ++p)
{
//Horizontal
gcpRendD3D->FX_PushRenderTarget(0, pTempRT, NULL);
gcpRendD3D->FX_SetActiveRenderTargets(false);// Avoiding invalid 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->RT_SetViewport(0, 0, pTex->GetWidth(), pTex->GetHeight());
// !force updating constants per-pass! (dx10..)
CShaderMan::s_shPostEffects->FXBeginPass(0);
pTex->Apply(0, CTexture::GetTexState(sTexState));
if (pMask)
{
pMask->Apply(1, CTexture::GetTexState(sTexState));
}
CShaderMan::s_shPostEffects->FXSetVSFloat(pParam1Name, pHParams, nHalfSamples);
DrawFullScreenTri(pTex->GetWidth(), pTex->GetHeight());
CShaderMan::s_shPostEffects->FXEndPass();
gcpRendD3D->FX_PopRenderTarget(0);
//Vertical
gcpRendD3D->FX_PushRenderTarget(0, pTex, NULL);
gcpRendD3D->FX_SetActiveRenderTargets(false);// Avoiding invalid 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->RT_SetViewport(0, 0, pTex->GetWidth(), pTex->GetHeight());
// !force updating constants per-pass! (dx10..)
CShaderMan::s_shPostEffects->FXBeginPass(0);
CShaderMan::s_shPostEffects->FXSetVSFloat(pParam1Name, pVParams, nHalfSamples);
pTempRT->Apply(0, CTexture::GetTexState(sTexState));
if (pMask)
{
pMask->Apply(1, CTexture::GetTexState(sTexState));
}
DrawFullScreenTri(pTex->GetWidth(), pTex->GetHeight());
CShaderMan::s_shPostEffects->FXEndPass();
gcpRendD3D->FX_PopRenderTarget(0);
}
CShaderMan::s_shPostEffects->FXEnd();
// ShEndPass( );
// Restore previous viewport
gcpRendD3D->RT_SetViewport(iTempX, iTempY, iWidth, iHeight);
//release dyntexture
SAFE_DELETE(tpBlurTemp);
gRenDev->m_RP.m_FlagsShader_RT = nSaveFlagsShader_RT;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
void SD3DPostEffectsUtils::BeginStencilPrePass(const bool bAddToStencil, const bool bDebug, const bool bResetStencil, const uint8 nStencilRefReset)
{
if (!bAddToStencil && !bResetStencil)
{
gcpRendD3D->m_nStencilMaskRef++;
}
if (gcpRendD3D->m_nStencilMaskRef > STENC_MAX_REF)
{
gcpRendD3D->EF_ClearTargetsImmediately(FRT_CLEAR_STENCIL);
gcpRendD3D->m_nStencilMaskRef = 1;
}
gcpRendD3D->FX_SetStencilState(
STENC_FUNC(FSS_STENCFUNC_ALWAYS) |
STENCOP_FAIL(FSS_STENCOP_REPLACE) |
STENCOP_ZFAIL(FSS_STENCOP_REPLACE) |
STENCOP_PASS(FSS_STENCOP_REPLACE),
bResetStencil ? nStencilRefReset : gcpRendD3D->m_nStencilMaskRef, 0xFFFFFFFF, 0xFFFF
);
gcpRendD3D->FX_SetState(GS_STENCIL | GS_NODEPTHTEST | (!bDebug ? GS_COLMASK_NONE : 0));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void SD3DPostEffectsUtils::EndStencilPrePass()
{
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void SD3DPostEffectsUtils::SetupStencilStates(int32 nStFunc)
{
if (nStFunc < 0)
{
return;
}
gcpRendD3D->FX_SetStencilState(
STENC_FUNC(nStFunc) |
STENCOP_FAIL(FSS_STENCOP_KEEP) |
STENCOP_ZFAIL(FSS_STENCOP_KEEP) |
STENCOP_PASS(FSS_STENCOP_KEEP),
gcpRendD3D->m_nStencilMaskRef, 0xFFFFFFFF, 0xFFFFFFFF);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void SD3DPostEffectsUtils::SetFillModeSolid(bool bEnable)
{
if (bEnable)
{
if (gcpRendD3D->GetWireframeMode() > R_SOLID_MODE)
{
SStateRaster RS = gcpRendD3D->m_StatesRS[gcpRendD3D->m_nCurStateRS];
RS.Desc.FillMode = D3D11_FILL_SOLID;
gcpRendD3D->SetRasterState(&RS);
}
}
else
{
if (gcpRendD3D->GetWireframeMode() == R_WIREFRAME_MODE)
{
SStateRaster RS = gcpRendD3D->m_StatesRS[gcpRendD3D->m_nCurStateRS];
RS.Desc.FillMode = D3D11_FILL_WIREFRAME;
gcpRendD3D->SetRasterState(&RS);
}
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
void SD3DPostEffectsUtils::DrawQuadFS(CShader* pShader, bool bOutputCamVec, int nWidth, int nHeight, float x0, float y0, float x1, float y1, float z)
{
const Vec4 cQuadConst(1.0f / (float) nWidth, 1.0f / (float) nHeight, z, 1.0f);
pShader->FXSetVSFloat(m_pQuadParams, &cQuadConst, 1);
const Vec4 cQuadPosConst(x0, y0, x1, y1);
pShader->FXSetVSFloat(m_pQuadPosParams, &cQuadPosConst, 1);
if (bOutputCamVec)
{
UpdateFrustumCorners();
const Vec4 vLT(m_vLT, 1.0f);
pShader->FXSetVSFloat(m_pFrustumLTParams, &vLT, 1);
const Vec4 vLB(m_vLB, 1.0f);
pShader->FXSetVSFloat(m_pFrustumLBParams, &vLB, 1);
const Vec4 vRT(m_vRT, 1.0f);
pShader->FXSetVSFloat(m_pFrustumRTParams, &vRT, 1);
const Vec4 vRB(m_vRB, 1.0f);
pShader->FXSetVSFloat(m_pFrustumRBParams, &vRB, 1);
}
gcpRendD3D->FX_Commit();
if (!FAILED(gcpRendD3D->FX_SetVertexDeclaration(0, eVF_P3F_C4B_T2F)))
{
gcpRendD3D->FX_SetVStream(0, gcpRendD3D->m_pQuadVB, 0, sizeof(SVF_P3F_C4B_T2F));
gcpRendD3D->FX_DrawPrimitive(eptTriangleStrip, 0, gcpRendD3D->m_nQuadVBSize);
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
bool CD3D9Renderer::FX_PostProcessScene(bool bEnable)
{
AZ_TRACE_METHOD();
if (!gcpRendD3D)
{
return false;
}
if (bEnable)
{
PostProcessUtils().Create();
}
else
if (!CRenderer::CV_r_PostProcess && CTexture::s_ptexBackBuffer)
{
PostProcessUtils().Release();
}
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
void CD3D9Renderer::GetReprojectionMatrix(Matrix44A& matReproj,
const Matrix44A& matView,
const Matrix44A& matProj,
const Matrix44A& matPrevView,
const Matrix44A& matPrevProj,
float fFarPlane) const
{
// Current camera screen-space to projection-space
const Matrix44A matSc2Pc
(2.f, 0.f, -1.f, 0.f,
0.f, 2.f, -1.f, 0.f,
0.f, 0.f, 1.f, 0.f,
0.f, 0.f, 0.f, 1.f / fFarPlane);
// Current camera view-space to projection-space
const Matrix44A matVc2Pc
(matProj.m00, 0.f, 0.f, 0.f,
0.f, matProj.m11, 0.f, 0.f,
0.f, 0.f, 1.f, 0.f,
0.f, 0.f, 0.f, 1.f);
// Current camera projection-space to world-space
const Matrix44A matPc2Wc = (matVc2Pc * matView).GetInverted();
// Previous camera view-space to projection-space
const Matrix44A matVp2Pp
(matPrevProj.m00, 0.f, 0.f, 0.f,
0.f, matPrevProj.m11, 0.f, 0.f,
0.f, 0.f, 1.f, 0.f,
0.f, 0.f, 0.f, 1.f);
// Previous camera world-space to projection-space
const Matrix44A matWp2Pp = matVp2Pp * matPrevView;
// Previous camera projection-space to texture-space
const Matrix44A matPp2Tp
(0.5f, 0.f, 0.5f, 0.f,
0.f, 0.5f, 0.5f, 0.f,
0.f, 0.f, 1.f, 0.f,
0.f, 0.f, 0.f, 1.f);
// Final reprojection matrix (from current camera screen-space to previous camera texture-space)
matReproj = matPp2Tp * matWp2Pp * matPc2Wc * matSc2Pc;
}
void CD3D9Renderer::UpdatePreviousFrameMatrices()
{
PreviousFrameMatrixSet& matrices = m_PreviousFrameMatrixSets[m_CurViewportID][m_CurRenderEye];
matrices.m_WorldViewPosition = GetViewParameters().vOrigin;
matrices.m_ViewMatrix = m_CameraMatrix;
matrices.m_ViewNoTranslateMatrix = m_CameraZeroMatrix[m_RP.m_nProcessThreadID];
matrices.m_ProjMatrix = m_ProjNoJitterMatrix;
// Use next frame jitter so that motion vector calculation is stable with jitter. While it would
// be best to remove jitter entirely, the shaders are already computing the clip space position of
// the current fragment with jitter, so this a pragmatic compromise.
SubpixelJitter::Sample sample = SubpixelJitter::EvaluateSample(SPostEffectsUtils::m_iFrameCounter + 1, (SubpixelJitter::Pattern)CV_r_AntialiasingTAAJitterPattern);
Vec2 subpixelOffsetClipSpace;
subpixelOffsetClipSpace.x = ((sample.m_subpixelOffset.x * 2.0) / (float)m_width) / m_RP.m_CurDownscaleFactor.x;
subpixelOffsetClipSpace.y = ((sample.m_subpixelOffset.y * 2.0) / (float)m_height) / m_RP.m_CurDownscaleFactor.y;
matrices.m_ProjMatrix.m20 += subpixelOffsetClipSpace.x;
matrices.m_ProjMatrix.m21 += subpixelOffsetClipSpace.y;
matrices.m_ViewProjMatrix = m_ViewProjNoJitterMatrix;
matrices.m_ViewProjNoTranslateMatrix = m_CameraZeroMatrix[m_RP.m_nProcessThreadID] * matrices.m_ProjMatrix;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
void CPostEffectsMgr::Begin()
{
PostProcessUtils().Log("### POST-PROCESSING BEGINS ### ");
PostProcessUtils().m_pTimer = gEnv->pTimer;
static EShaderQuality nPrevShaderQuality = eSQ_Low;
static ERenderQuality nPrevRenderQuality = eRQ_Low;
EShaderQuality nShaderQuality = (EShaderQuality) gcpRendD3D->EF_GetShaderQuality(eST_PostProcess);
ERenderQuality nRenderQuality = gRenDev->m_RP.m_eQuality;
if (nPrevShaderQuality != nShaderQuality || nPrevRenderQuality != nRenderQuality)
{
CPostEffectsMgr::Reset(true);
nPrevShaderQuality = nShaderQuality;
nPrevRenderQuality = nRenderQuality;
}
gcpRendD3D->ResetToDefault();
SPostEffectsUtils::m_pCurDepthSurface = &gcpRendD3D->m_DepthBufferOrig;
gcpRendD3D->RT_SetViewport(0, 0, gcpRendD3D->GetWidth(), gcpRendD3D->GetHeight());
SPostEffectsUtils::m_fWaterLevel = gRenDev->m_p3DEngineCommon.m_OceanInfo.m_fWaterLevel;
PostProcessUtils().SetFillModeSolid(true);
PostProcessUtils().UpdateOverscanBorderAspectRatio();
}
void CPostEffectsMgr::End()
{
gcpRendD3D->RT_SetViewport(PostProcessUtils().m_pScreenRect.left, PostProcessUtils().m_pScreenRect.top, PostProcessUtils().m_pScreenRect.right, PostProcessUtils().m_pScreenRect.bottom);
gcpRendD3D->FX_ResetPipe();
PostProcessUtils().SetFillModeSolid(false);
const uint32 nThreadID = gRenDev->m_RP.m_nProcessThreadID;
int recursiveLevel = SRendItem::m_RecurseLevel[nThreadID];
assert(recursiveLevel >= 0);
#if AZ_RENDER_TO_TEXTURE_GEM_ENABLED
if (gRenDev->m_RP.m_TI[nThreadID].m_PersFlags & RBPF_RENDER_SCENE_TO_TEXTURE)
{
return;
}
#endif // if AZ_RTT_ENABLE
gcpRendD3D->UpdatePreviousFrameMatrices();
const int kFloatMaxContinousInt = 0x1000000; // 2^24
const bool bStereo = gcpRendD3D->GetS3DRend().IsStereoEnabled();
if (!bStereo || (bStereo && gRenDev->m_CurRenderEye == STEREO_EYE_RIGHT))
{
SPostEffectsUtils::m_iFrameCounter = (SPostEffectsUtils::m_iFrameCounter + 1) % kFloatMaxContinousInt;
}
PostProcessUtils().Log("### POST-PROCESSING ENDS ### ");
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
bool CREPostProcess:: mfDraw([[maybe_unused]] CShader* ef, [[maybe_unused]] SShaderPass* sfm)
{
CPostEffectsMgr* pPostMgr = PostEffectMgr();
IF (!gcpRendD3D || !CRenderer::CV_r_PostProcess || pPostMgr->GetEffects().empty() || gcpRendD3D->GetWireframeMode() > R_SOLID_MODE, 0)
{
return 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))
{
return 0;
}
if (gcpRendD3D->m_bDeviceLost)
{
return 0;
}
IF (!CShaderMan::s_shPostEffects, 0)
{
return 0;
}
IF (!CTexture::IsTextureExist(CTexture::s_ptexBackBuffer), 0)
{
return 0;
}
IF (!CTexture::IsTextureExist(CTexture::s_ptexSceneTarget), 0)
{
return 0;
}
PROFILE_LABEL_SCOPE("POST EFFECTS");
pPostMgr->Begin();
gcpRendD3D->FX_ApplyShaderQuality(eST_PostProcess);
for (CPostEffectItor pItor = pPostMgr->GetEffects().begin(), pItorEnd = pPostMgr->GetEffects().end(); pItor != pItorEnd; ++pItor)
{
CPostEffect* pCurrEffect = (*pItor);
if (pCurrEffect->GetRenderFlags() & PSP_REQUIRES_UPDATE)
{
pCurrEffect->Update();
}
}
#ifndef _RELEASE
CPostEffectDebugVec& activeEffects = pPostMgr->GetActiveEffectsDebug();
CPostEffectDebugVec& activeParams = pPostMgr->GetActiveEffectsParamsDebug();
#endif
AZStd::vector<CPostEffect*> effectsToRender;
for (CPostEffectItor pItor = pPostMgr->GetEffects().begin(), pItorEnd = pPostMgr->GetEffects().end(); pItor != pItorEnd; ++pItor)
{
CPostEffect* effectToPreprocess = (*pItor);
if (effectToPreprocess->Preprocess())
{
effectsToRender.push_back(effectToPreprocess);
}
}
for (auto effectIter = effectsToRender.begin(); effectIter != effectsToRender.end(); ++effectIter)
{
CPostEffect* currentEffect = (*effectIter);
uint32 nRenderFlags = currentEffect->GetRenderFlags();
if (nRenderFlags & PSP_UPDATE_BACKBUFFER)
{
PostProcessUtils().CopyScreenToTexture(CTexture::s_ptexBackBuffer);
}
if (nRenderFlags & PSP_UPDATE_SCENE_SPECULAR)
{
bool optimizeRT = CRenderer::CV_r_SlimGBuffer == 1;
// When optimization is on, we use a single channel format texture for specular. This copy requires full RGBA so use normal map render target instead.
PostProcessUtils().CopyScreenToTexture(optimizeRT ? CTexture::s_ptexSceneNormalsMap : CTexture::s_ptexSceneSpecular);
}
# ifndef _RELEASE
SPostEffectsDebugInfo* pDebugInfo = NULL;
for (uint32 i = 0, nNumEffects = activeEffects.size(); i < nNumEffects; ++i)
{
if ((pDebugInfo = &activeEffects[i]) && pDebugInfo->pEffect == currentEffect)
{
pDebugInfo->fTimeOut = POSTSEFFECTS_DEBUGINFO_TIMEOUT;
break;
}
pDebugInfo = NULL;
}
if (pDebugInfo == NULL)
{
activeEffects.push_back(SPostEffectsDebugInfo(currentEffect));
}
#endif
if (CRenderer::CV_r_SkipNativeUpscale > 0 && AZStd::next(effectIter) == effectsToRender.end())
{
gcpRendD3D->FX_PopRenderTarget(0);
gcpRendD3D->RT_SetViewport(0,0, gcpRendD3D->GetNativeWidth(), gcpRendD3D->GetNativeHeight());
gcpRendD3D->FX_SetRenderTarget(0, gcpRendD3D->GetBackBuffer(), &gcpRendD3D->m_DepthBufferOrigMSAA);
gcpRendD3D->FX_SetActiveRenderTargets();
}
currentEffect->Render();
}
# ifndef _RELEASE
if (CRenderer::CV_r_AntialiasingModeDebug > 0)
{
float mx = CTexture::s_ptexBackBuffer->GetWidth() >> 1, my = CTexture::s_ptexBackBuffer->GetHeight() >> 1;
AZ::Vector2 systemCursorPositionNormalized = AZ::Vector2::CreateZero();
AzFramework::InputSystemCursorRequestBus::EventResult(systemCursorPositionNormalized,
AzFramework::InputDeviceMouse::Id,
&AzFramework::InputSystemCursorRequests::GetSystemCursorPositionNormalized);
mx = systemCursorPositionNormalized.GetX() * gEnv->pRenderer->GetWidth();
my = systemCursorPositionNormalized.GetY() * gEnv->pRenderer->GetHeight();
PostProcessUtils().CopyScreenToTexture(CTexture::s_ptexBackBuffer);
static CCryNameTSCRC pszTechName("DebugPostAA");
GetUtils().ShBeginPass(CShaderMan::s_shPostAA, pszTechName, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
gcpRendD3D->SetCullMode(R_CULL_NONE);
gcpRendD3D->FX_SetState(GS_NODEPTHTEST);
const Vec4 vDebugParams(mx, my, 1.f, max(1.0f, (float) CRenderer::CV_r_AntialiasingModeDebug));
static CCryNameR pszDebugParams("vDebugParams");
CShaderMan::s_shPostAA->FXSetPSFloat(pszDebugParams, &vDebugParams, 1);
GetUtils().SetTexture(CTexture::s_ptexBackBuffer, 0, FILTER_POINT);
SD3DPostEffectsUtils::DrawFullScreenTriWPOS(CTexture::s_ptexBackBuffer->GetWidth(), CTexture::s_ptexBackBuffer->GetHeight());
GetUtils().ShEndPass();
}
if (!activeEffects.empty() && CRenderer::CV_r_PostProcess >= 2) // Debug output for active post effects
{
SDrawTextInfo pDrawTexInfo;
if (CRenderer::CV_r_PostProcess >= 2)
{
int nPosY = 20;
pDrawTexInfo.color[0] = pDrawTexInfo.color[2] = 0.0f;
pDrawTexInfo.color[1] = 1.0f;
gcpRendD3D->Draw2dText(30, nPosY += 15, "Active post effects:", pDrawTexInfo);
pDrawTexInfo.color[0] = pDrawTexInfo.color[1] = pDrawTexInfo.color[2] = 1.0f;
for (uint32 i = 0, nNumEffects = activeEffects.size(); i < nNumEffects; ++i)
{
SPostEffectsDebugInfo& debugInfo = activeEffects[i];
if (debugInfo.fTimeOut > 0.0f)
{
gcpRendD3D->Draw2dText(30, nPosY += 10, debugInfo.pEffect->GetName(), pDrawTexInfo);
}
debugInfo.fTimeOut -= gEnv->pTimer->GetFrameTime();
}
}
if (CRenderer::CV_r_PostProcess == 3)
{
StringEffectMap* pEffectsParamsUpdated = pPostMgr->GetDebugParamsUsedInFrame();
if (pEffectsParamsUpdated)
{
if (!pEffectsParamsUpdated->empty())
{
StringEffectMapItor pEnd = pEffectsParamsUpdated->end();
for (StringEffectMapItor pItor = pEffectsParamsUpdated->begin(); pItor != pEnd; ++pItor)
{
SPostEffectsDebugInfo* pDebugInfo = NULL;
for (uint32 p = 0, nNumParams = activeParams.size(); p < nNumParams; ++p)
{
if ((pDebugInfo = &activeParams[p]) && pDebugInfo->szParamName == (pItor->first))
{
pDebugInfo->fTimeOut = POSTSEFFECTS_DEBUGINFO_TIMEOUT;
break;
}
pDebugInfo = NULL;
}
if (pDebugInfo == NULL)
{
activeParams.push_back(SPostEffectsDebugInfo((pItor->first), (pItor->second != 0) ? pItor->second->GetParam() : 0.0f));
}
}
pEffectsParamsUpdated->clear();
}
int nPosX = 250, nPosY = 5;
pDrawTexInfo.color[0] = pDrawTexInfo.color[2] = 0.0f;
pDrawTexInfo.color[1] = 1.0f;
gcpRendD3D->Draw2dText(nPosX, nPosY += 15, "Frame parameters:", pDrawTexInfo);
pDrawTexInfo.color[0] = pDrawTexInfo.color[1] = pDrawTexInfo.color[2] = 1.0f;
for (uint32 p = 0, nNumParams = activeParams.size(); p < nNumParams; ++p)
{
SPostEffectsDebugInfo& debugInfo = activeParams[p];
if (debugInfo.fTimeOut > 0.0f)
{
char pNameAndValue[128];
sprintf_s(pNameAndValue, "%s: %.4f\n", debugInfo.szParamName.c_str(), debugInfo.fParamVal);
gcpRendD3D->Draw2dText(nPosX, nPosY += 10, pNameAndValue, pDrawTexInfo);
}
debugInfo.fTimeOut -= gEnv->pTimer->GetFrameTime();
}
}
}
}
#endif
pPostMgr->End();
return 1;
}