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/RTT/RTTContext.cpp

615 lines
23 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.
*
*/
#include "RenderDll_precompiled.h"
#if AZ_RENDER_TO_TEXTURE_GEM_ENABLED
#include "AzCore/std/utils.h"
#include <AzCore/Serialization/SerializeContext.h>
#include "DriverD3D.h"
#include "RTTContext.h"
#include "RTTSwappableRenderTarget.h"
namespace AzRTT
{
RenderContext::RenderContext(RenderContextId id, const RenderContextConfig& config)
: RenderContext(id)
{
SetConfig(config);
}
RenderContext::RenderContext(RenderContextId id)
: m_viewport(0, 0, 0, 0)
, m_renderContextId(id)
, m_depthTarget(nullptr)
, m_depthTargetMSAA(nullptr)
, m_zBufferDepthReadOnlySRV(nullptr)
, m_zBufferStencilReadOnlySRV(nullptr)
, m_active(false)
, m_errorState(ErrorState::OK)
{
struct cvarIntEntry {
const char* name;
int* variable;
};
const cvarIntEntry icvars[] =
{
{"e_GsmLodsNum", nullptr},
{"e_Dissolve", nullptr},
{"e_CoverageBuffer", nullptr},
{"e_StatObjBufferRenderTasks", nullptr},
{"r_FinalOutputAlpha", &(gRenDev->CV_r_FinalOutputAlpha)},
{"r_FinalOutputsRGB", &(gRenDev->CV_r_FinalOutputsRGB)},
{"r_Flares", &(gRenDev->CV_r_flares)},
{"r_sunshafts", &(gRenDev->CV_r_sunshafts)},
{"r_AntialiasingMode", &(gRenDev->CV_r_AntialiasingMode)},
{"r_MotionBlur", &(gRenDev->CV_r_MotionBlur)},
{"r_DepthOfField", &(gRenDev->CV_r_dof)}
};
for (const cvarIntEntry& cvar : icvars)
{
m_iCVars.insert({ cvar.name, SwappableCVar<int>(cvar.name, cvar.variable) });
}
struct cvarFloatEntry {
const char* name;
float* variable;
};
const cvarFloatEntry fcvars[] =
{
{"e_GsmRange", nullptr},
{"e_GsmRangeStep", nullptr}
};
for (const cvarFloatEntry& cvar : fcvars)
{
m_fCVars.insert({ cvar.name, SwappableCVar<float>(cvar.name, cvar.variable) });
}
}
RenderContext::~RenderContext()
{
SetActive(false);
Release();
}
void RenderContext::Release()
{
m_swappableRenderTargets.clear();
// these depth targets are dynamic and managed by the renderer so do not call Release()
m_depthTarget = nullptr;
m_depthTargetMSAA = nullptr;
if (m_zBufferDepthReadOnlySRV)
{
m_zBufferDepthReadOnlySRV->Release();
m_zBufferDepthReadOnlySRV = nullptr;
}
if (m_zBufferStencilReadOnlySRV)
{
m_zBufferStencilReadOnlySRV->Release();
m_zBufferStencilReadOnlySRV = nullptr;
}
}
bool RenderContext::ResourcesCreated() const
{
return !m_swappableRenderTargets.empty() && m_depthTarget && m_depthTargetMSAA && m_zBufferDepthReadOnlySRV && m_zBufferStencilReadOnlySRV;
}
bool RenderContext::Initialize()
{
// it may take an extra frame to create resources, don't re-create them if we've already started
if (!m_swappableRenderTargets.empty())
{
return IsValid();
}
m_viewport.nWidth = m_config.m_width;
m_viewport.nHeight = m_config.m_height;
// attempt to create render targets
if (CreateRenderTargets(m_viewport.nWidth, m_viewport.nHeight))
{
// attempt to create depth targets
CreateDepthTargets(m_viewport.nWidth, m_viewport.nHeight);
}
// if we failed to create all resources then release them
if (!IsValid())
{
Release();
}
return IsValid();
}
bool RenderContext::SetActive(bool active)
{
if (active && !IsValid())
{
// we cannot activate an invalid context
return false;
}
if (active != m_active)
{
m_active = active;
SetActiveMainThread(active);
// swap buffers and apply/revert render-thread specific settings
gRenDev->m_pRT->EnqueueRenderCommand([this, active]()
{
SetActiveRenderThread(active);
});
// in single threaded mode (editor) we can check here for
// invalid context and revert to inactive state
if (active && !IsValid())
{
m_active = false;
SetActiveMainThread(false);
}
}
return IsValid();
}
void RenderContext::SetActiveMainThread(bool active)
{
if (active)
{
if (m_config.m_shadowsEnabled)
{
if (m_config.m_shadowsNumCascades > -1)
{
m_iCVars["e_GsmLodsNum"].Swap(m_config.m_shadowsNumCascades);
}
if (m_config.m_shadowsGSMRange > 0.f)
{
m_fCVars["e_GsmRange"].Swap(m_config.m_shadowsGSMRange);
}
if (m_config.m_shadowsGSMRangeStep > 0.f)
{
m_fCVars["e_GsmRangeStep"].Swap(m_config.m_shadowsGSMRangeStep);
}
}
m_iCVars["e_Dissolve"].Disable();
m_iCVars["e_CoverageBuffer"].Disable();
m_iCVars["e_StatObjBufferRenderTasks"].Disable();
// set renderer width/height
gRenDev->SetWidth(m_viewport.nWidth);
gRenDev->SetHeight(m_viewport.nHeight);
}
else
{
if (m_config.m_shadowsEnabled)
{
m_iCVars["e_GsmLodsNum"].Restore();
m_fCVars["e_GsmRange"].Restore();
m_fCVars["e_GsmRangeStep"].Restore();
}
m_iCVars["e_Dissolve"].Restore();
m_iCVars["e_CoverageBuffer"].Restore();
m_iCVars["e_StatObjBufferRenderTasks"].Restore();
if (!IsValid())
{
// if this context is invalid then restore the renderer
// width/height to a known good state
gRenDev->SetWidth(m_previousSettings.m_viewport.nWidth);
gRenDev->SetHeight(m_previousSettings.m_viewport.nHeight);
}
}
}
void RenderContext::SetActiveRenderThread(bool active)
{
AZ_Assert(IsValid(), "RenderContext is not valid and cannot be activated");
if (active && !ResourcesCreated())
{
// initialization can fail for various reasons including running out of memory
if (!Initialize())
{
return;
}
}
AZ_Assert(m_viewport.nWidth != 0 && m_viewport.nHeight != 0, "Invalid RenderContext viewport size, width and height must be greater than zero.");
for (SwappableRenderTarget& swappableRenderTarget : m_swappableRenderTargets)
{
swappableRenderTarget.Swap();
}
// handle special cases (aliases)
CTexture::s_ptexCurrSceneTarget = CTexture::s_ptexSceneTarget;
CTexture::s_ptexCurrentSceneDiffuseAccMap = CTexture::s_ptexSceneDiffuseAccMap;
// swap viewports and depth surfaces
if (active)
{
// backup/swap viewport
m_previousSettings.m_viewport = gRenDev->m_MainViewport;
gRenDev->m_MainViewport = m_viewport;
// only apply width/height in multithreaded mode because engine thread
// does this too
if (gRenDev->CV_r_multithreaded)
{
gRenDev->SetWidth(m_viewport.nWidth);
gRenDev->SetHeight(m_viewport.nHeight);
}
// backup/swap depth targets
m_previousSettings.m_depthOrig = gcpRendD3D->m_DepthBufferOrig;
m_previousSettings.m_depthMSAA = gcpRendD3D->m_DepthBufferOrigMSAA;
gcpRendD3D->m_DepthBufferOrig = *m_depthTarget;
gcpRendD3D->m_DepthBufferOrigMSAA = *m_depthTargetMSAA;
m_iCVars["r_FinalOutputAlpha"].Swap(static_cast<int>(m_config.m_alphaMode));
m_iCVars["r_FinalOutputsRGB"].Swap(m_config.m_sRGBWrite ? 1 : 0);
// Not supported yet because they pollute the main render pass - lots of shared occlusion code
m_iCVars["r_Flares"].Swap(0);
// Not supported yet because the occlusion queries used for visibilty are async and can cause flickering.
m_iCVars["r_sunshafts"].Swap(0);
// rtt cvar overrides - useful for debugging
static ICVar* rttAA = gEnv->pConsole->GetCVar("rtt_aa");
if (rttAA && rttAA->GetIVal() > -1)
{
m_iCVars["r_AntialiasingMode"].Swap(rttAA->GetIVal());
}
else
{
m_iCVars["r_AntialiasingMode"].Swap(m_config.m_aaMode);
}
static ICVar* rttMotionBlur = gEnv->pConsole->GetCVar("rtt_motionblur");
if (rttMotionBlur && rttMotionBlur->GetIVal() > -1)
{
m_iCVars["r_MotionBlur"].Swap(rttMotionBlur->GetIVal());
}
else
{
m_iCVars["r_MotionBlur"].Swap(m_config.m_motionBlurEnabled ? 2 : 0);
}
static ICVar* rttDepthOfField = gEnv->pConsole->GetCVar("rtt_dof");
if (rttDepthOfField && rttDepthOfField->GetIVal() > -1)
{
m_iCVars["r_DepthOfField"].Swap(rttDepthOfField->GetIVal());
}
else
{
m_iCVars["r_DepthOfField"].Swap(m_config.m_depthOfFieldEnabled ? DOF_DEFAULT_VAL : 0);
}
}
else
{
gRenDev->m_MainViewport = m_previousSettings.m_viewport;
gcpRendD3D->m_DepthBufferOrig = m_previousSettings.m_depthOrig;
gcpRendD3D->m_DepthBufferOrigMSAA = m_previousSettings.m_depthMSAA;
// handle aliases
CTexture::s_ptexCurLumTexture = nullptr;
// restore settings
m_iCVars["r_FinalOutputAlpha"].Restore();
m_iCVars["r_FinalOutputsRGB"].Restore();
m_iCVars["r_Flares"].Restore();
m_iCVars["r_sunshafts"].Restore();
m_iCVars["r_MotionBlur"].Restore();
m_iCVars["r_DepthOfField"].Restore();
m_iCVars["r_AntialiasingMode"].Restore();
}
// swap resource views
if (m_zBufferDepthReadOnlySRV)
{
AZStd::swap(gcpRendD3D->m_pZBufferDepthReadOnlySRV, m_zBufferDepthReadOnlySRV);
}
if (m_zBufferStencilReadOnlySRV)
{
AZStd::swap(gcpRendD3D->m_pZBufferStencilReadOnlySRV, m_zBufferStencilReadOnlySRV);
}
}
void RenderContext::SetConfig(const RenderContextConfig& config)
{
uint32_t width = config.m_width;
uint32_t height = config.m_height;
// ensure the width/height used is supported by the current hardware
const int maxTextureSize = gcpRendD3D->GetMaxTextureSize();
if (maxTextureSize > 0)
{
width = min(width, static_cast<uint32_t>(maxTextureSize));
height = min(height, static_cast<uint32_t>(maxTextureSize));
}
// assume valid until we try to resize/create resources and fail
m_errorState = ErrorState::OK;
if (ResourcesCreated() && (m_config.m_width != width || m_config.m_height != height))
{
ResizeRenderTargets(width, height);
}
m_config = config;
m_config.m_width = width;
m_config.m_height = height;
m_viewport.nWidth = width;
m_viewport.nHeight = height;
}
bool RenderContext::CreateRenderTargets(AZ::u32 width, AZ::u32 height)
{
// when all engine textures are managed by CTextureManager
// this can be replaced
struct swappableRenderTarget
{
CTexture** texture;
uint32_t scale;
};
// NOTE: some of these textures are swapped because they are used by
// the render to texture pass, and others are swapped to avoid
// overwriting temporal textures used in the main pass
const swappableRenderTarget targets[] = {
{ &CTexture::s_ptexSceneDiffuse, 1 },
{ &CTexture::s_ptexSceneDiffuseAccMap, 1 }, // ignore the s_ptexCurrentSceneDiffuseAccMap alias
{ &CTexture::s_ptexSceneNormalsBent, 1 },
{ &CTexture::s_ptexSceneNormalsMap , 1},
{ &CTexture::s_ptexSceneSpecular, 1 },
{ &CTexture::s_ptexSceneSpecularAccMap, 1 },
{ &CTexture::s_ptexSceneTarget, 1 },
{ &CTexture::s_ptexSceneTargetR11G11B10F[0], 1 },
{ &CTexture::s_ptexSceneTargetR11G11B10F[1], 1 },
{ &CTexture::s_ptexShadowMask, 1 },
{ &CTexture::s_ptexHDRTarget, 1 },
{ &CTexture::s_ptexHDRTargetPrev, 1 },
{ &CTexture::s_ptexSceneCoCHistory[0], 1 },
{ &CTexture::s_ptexSceneCoCHistory[1], 1 },
// motion blur
{ &CTexture::s_ptexVelocity, 1 },
{ &CTexture::s_ptexVelocityObjects[0], 1 },
{ &CTexture::s_ptexStereoL, 1 },
{ &CTexture::s_ptexStereoR, 1 },
{ &CTexture::s_ptexBackBuffer, 1 },
{ &CTexture::s_ptexPrevBackBuffer[0][0], 1 },
{ &CTexture::s_ptexPrevBackBuffer[1][0], 1 },
// HDR Targets scaled down
{ &CTexture::s_ptexHDRTargetScaled[0], 2 },
{ &CTexture::s_ptexHDRTargetScaled[1], 4 },
{ &CTexture::s_ptexHDRTargetScaled[2], 8 },
{ &CTexture::s_ptexHDRTargetScaled[3], 8 },
{ &CTexture::s_ptexBackBufferScaled[0], 2 },
{ &CTexture::s_ptexBackBufferScaled[1], 4 },
{ &CTexture::s_ptexBackBufferScaled[2], 8 },
{ &CTexture::s_ptexHDRDofLayers[0], 2 },
{ &CTexture::s_ptexHDRDofLayers[1], 2 },
{ &CTexture::s_ptexHDRTargetScaledTmp[0], 2 },
{ &CTexture::s_ptexHDRTargetScaledTmp[1], 4 },
//{ &CTexture::s_ptexHDRTargetScaledTmp[2], ? }, unused
{ &CTexture::s_ptexHDRTargetScaledTmp[3], 8 },
{ &CTexture::s_ptexHDRTargetScaledTempRT[0], 2 },
{ &CTexture::s_ptexHDRTargetScaledTempRT[1], 4 },
{ &CTexture::s_ptexHDRTargetScaledTempRT[2], 8 },
{ &CTexture::s_ptexHDRTargetScaledTempRT[3], 8 },
{ &CTexture::s_ptexHDRTempBloom[0], 4 },
{ &CTexture::s_ptexHDRTempBloom[1], 4 },
{ &CTexture::s_ptexHDRFinalBloom, 4 },
// SPostEffectsUtils used for things like lense flares
{ &CTexture::s_ptexBackBufferScaledTemp[0], 2 },
{ &CTexture::s_ptexBackBufferScaledTemp[1], 4 },
// Water volume reflections
{ &CTexture::s_ptexWaterVolumeRefl[0], 2 },
{ &CTexture::s_ptexWaterVolumeRefl[1], 2 },
// ZTargets
{ &CTexture::s_ptexZTarget, 1 },
{ &CTexture::s_ptexZTargetScaled, 2 },
{ &CTexture::s_ptexZTargetScaled2, 4 }
};
for (const swappableRenderTarget& target : targets)
{
m_swappableRenderTargets.push_back({ target.texture });
m_swappableRenderTargets.back().CreateRenderTargetCopy(width, height, target.scale, m_renderContextId);
}
// Depth of field COC
uint32_t scale = 1;
for (int i = 0; i < MIN_DOF_COC_K; i++)
{
m_swappableRenderTargets.push_back({ &CTexture::s_ptexSceneCoC[i] });
scale = 2 * (i + 1);
m_swappableRenderTargets.back().CreateRenderTargetCopy(width, height, scale, m_renderContextId);
}
scale = 1;
// Flare occlusion queries
for (int i = 0; i < MAX_OCCLUSION_READBACK_TEXTURES; i++)
{
m_swappableRenderTargets.push_back({ &CTexture::s_ptexFlaresOcclusionRing[i] });
m_swappableRenderTargets.back().CreateRenderTargetCopy(m_renderContextId);
}
m_swappableRenderTargets.push_back({ &CTexture::s_ptexFlaresGather });
m_swappableRenderTargets.back().CreateRenderTargetCopy(m_renderContextId);
// HDR adapted luminance render targets
for (int i = 0; i < 8; ++i)
{
m_swappableRenderTargets.push_back({ &CTexture::s_ptexHDRAdaptedLuminanceCur[i] });
m_swappableRenderTargets.back().CreateRenderTargetCopy(1, 1, scale, m_renderContextId);
}
for (int i = 0; i < NUM_HDR_TONEMAP_TEXTURES; i++)
{
int size = 1 << (2 * i);
m_swappableRenderTargets.push_back({ &CTexture::s_ptexHDRToneMaps[i] });
m_swappableRenderTargets.back().CreateRenderTargetCopy(size, size, scale, m_renderContextId);
}
for (int i = 0; i < MAX_GPU_NUM; ++i)
{
m_swappableRenderTargets.push_back({ &CTexture::s_ptexHDRMeasuredLuminance[i] });
m_swappableRenderTargets.back().CreateRenderTargetCopy(1,1, scale, m_renderContextId);
}
// velocity tiles
m_swappableRenderTargets.push_back({ &CTexture::s_ptexVelocityTiles[0] });
m_swappableRenderTargets.back().CreateRenderTargetCopy(20, height, scale, m_renderContextId);
m_swappableRenderTargets.push_back({ &CTexture::s_ptexVelocityTiles[1] });
m_swappableRenderTargets.back().CreateRenderTargetCopy(20, 20, scale, m_renderContextId);
m_swappableRenderTargets.push_back({ &CTexture::s_ptexVelocityTiles[2] });
m_swappableRenderTargets.back().CreateRenderTargetCopy(20, 20, scale, m_renderContextId);
if (!RenderTargetsAreValid())
{
m_errorState = ErrorState::ResourceCreationFailed;
AZ_Warning("RTTContext", false, "Failed to create render to texture textures for context %d", m_renderContextId);
}
return IsValid();
}
bool RenderContext::RenderTargetsAreValid() const
{
for (const auto& swappableRenderTarget : m_swappableRenderTargets)
{
if (!swappableRenderTarget.IsValid())
{
return false;
}
}
return true;
}
bool RenderContext::DepthTargetsAreValid() const
{
return m_depthTarget && m_depthTarget->pTarget && m_depthTargetMSAA && m_depthTargetMSAA->pTarget;
}
bool RenderContext::ResizeRenderTargets(AZ::u32 width, AZ::u32 height)
{
for (auto& swappableRenderTarget : m_swappableRenderTargets)
{
swappableRenderTarget.Resize(width, height);
}
if (!RenderTargetsAreValid())
{
m_errorState = ErrorState::ResourceCreationFailed;
AZ_Warning("RTTContext", false, "Failed to resize render to texture textures for context %d", m_renderContextId);
}
else
{
CreateDepthTargets(width, height);
}
// we might not have enough GPU memory to activate this context after a resize
if (!IsValid())
{
Release();
}
return IsValid();
}
bool RenderContext::CreateDepthTargets(AZ::u32 width, AZ::u32 height)
{
// render resources must be created on the render thread
// m_depthTarget and m_depthTargetMSAA may point to the same depth surface/target because
// r_msaa is off by default.
m_depthTarget = gcpRendD3D->FX_GetDepthSurface(width, height, false, true);
m_depthTargetMSAA = gcpRendD3D->FX_GetDepthSurface(width, height, true, true);
// create the depth resource view
if (gcpRendD3D->m_pZBufferDepthReadOnlySRV && m_depthTarget && m_depthTarget->pTarget && m_depthTargetMSAA && m_depthTargetMSAA->pTarget)
{
D3D11_TEXTURE2D_DESC descDepthStencil;
m_depthTargetMSAA->pTarget->GetDesc(&descDepthStencil);
D3DFormat nDepthStencilFormatTypeless = descDepthStencil.Format;
if (!CTexture::IsDeviceFormatTypeless(nDepthStencilFormatTypeless))
{
nDepthStencilFormatTypeless = CTexture::ConvertToTypelessFmt(nDepthStencilFormatTypeless);
}
D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc;
ZeroStruct(SRVDesc);
SRVDesc.Format = CTexture::ConvertToShaderResourceFmt(nDepthStencilFormatTypeless);
SRVDesc.ViewDimension = (descDepthStencil.SampleDesc.Count > 1) ? D3D11_SRV_DIMENSION_TEXTURE2DMS : D3D11_SRV_DIMENSION_TEXTURE2D;
SRVDesc.Texture2D.MipLevels = 1;
ID3D11ShaderResourceView* pNewResourceView = NULL;
HRESULT hr = gcpRendD3D->GetDevice().CreateShaderResourceView(m_depthTargetMSAA->pTarget, &SRVDesc, &pNewResourceView);
if (SUCCEEDED(hr))
{
hr = gcpRendD3D->GetDevice().CreateShaderResourceView(m_depthTargetMSAA->pTarget, &SRVDesc, &m_zBufferDepthReadOnlySRV);
AZ_Warning("RTTContext", SUCCEEDED(hr), "Failed to create resource shader view for RTT depth target %d", m_renderContextId);
}
else
{
AZ_Printf("RTTContext", "Failed to create resource shader view for RTT depth target %d", m_renderContextId);
}
}
// create our stencil buffer resource view if needed
if (gcpRendD3D->m_pZBufferStencilReadOnlySRV && m_depthTargetMSAA)
{
D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc;
gcpRendD3D->m_pZBufferStencilReadOnlySRV->GetDesc(&SRVDesc);
HRESULT hr = gcpRendD3D->GetDevice().CreateShaderResourceView(m_depthTargetMSAA->pTarget, &SRVDesc, &m_zBufferStencilReadOnlySRV);
AZ_Warning("RTTContext", SUCCEEDED(hr), "Failed to create stencil resource shader view for RTT depth target %d", m_renderContextId);
}
if (!DepthTargetsAreValid())
{
m_errorState = ErrorState::ResourceCreationFailed;
AZ_Warning("RTTContext", false, "Failed to create render to texture depth targets for context %d", m_renderContextId);
}
return IsValid();
}
}
#endif // if AZ_RENDER_TO_TEXTURE_GEM_ENABLED