Files
UnrealEngineUWP/Engine/Source/Runtime/Renderer/Private/TranslucentRendering.cpp
Marcus Wassmer edea678466 Copying //UE4/Dev-Rendering to //UE4/Dev-Main (Source: //UE4/Dev-Rendering @ 3072736)
#lockdown Nick.Penwarden
#rb none

==========================
MAJOR FEATURES + CHANGES
==========================

Change 3055495 on 2016/07/19 by Marc.Olano

	Allow Noise material node on mobile

	No reason to exclude mobile, except for Fast Gradient Noise, which uses 3D textures. Allow this node on ES2 for all of the other noise functions.

	#jira UE-33345

Change 3055602 on 2016/07/19 by Luke.Thatcher

	Fix crash bug in D3D11 RHI when selecting adapters.
	 - Array of adapter descriptors will get out of sync with the adapter index if any adapter is skipped (e.g. the Microsoft Basic Render Device).
	#jira UE-33236

Change 3055890 on 2016/07/19 by Daniel.Wright

	Improved the assert in LoadModuleChecked so we won't have to check the log to see which module it was

Change 3055891 on 2016/07/19 by Daniel.Wright

	Fixed Global Distance Field not dirtying previous object position on UpdateTransform - left behind a phantom shadow on teleports
	* This will effectively double partial distiance field update costs until clipping of the update regions is implemented

Change 3055892 on 2016/07/19 by Daniel.Wright

	Higher poly light source shapes drawn into reflection captures

Change 3055893 on 2016/07/19 by Daniel.Wright

	More info to 'Incompatible surface format' GNM assert

Change 3055904 on 2016/07/19 by Daniel.Wright

	Reflection environment normalization improvements
	* Indirect specular from reflection captures is now mixed with indirect diffuse from lightmaps based on roughness, such that a mirror surface will have no mixing.  Reflection captures now match other reflection methods like SSR and planar reflections much more closely.
	* When a stationary skylight is present, Reflection captures are now normalized as if the initial skylight will always be present, giving consistent results with static skylight reflections.  The skylight and reflection captures with sky removed used to be normalized separately, compacting the relative brightness between the sky and scene.
	* Added r.ReflectionEnvironmentLightmapMixing for debugging lightmap mixing issues.  This toggle was previously not possible due to prenormalizing the capture data.
	* The standard deferred reflection path (r.DoTiledReflections 0) can no longer match the results of the compute path or base pass reflections, as it would require MRT to accumulate the average brightness
	* Removed unused r.DiffuseFromCaptures
	* Cost of reflection environment on PS4 increased from 1.52ms -> 1.75ms with this change, but decreased back to 1.58ms by reducing tile size to 8x8

Change 3055905 on 2016/07/19 by Daniel.Wright

	Workaround for RTDF shadows not working on PS4 - manual clear of ObjectIndirectArguments instead of RHICmdList.ClearUAV

Change 3059486 on 2016/07/21 by Nick.Penwarden

	Testing #uecritical

Change 3060558 on 2016/07/21 by Daniel.Wright

	Fixed skylight with specified cubemap being black

Change 3061999 on 2016/07/22 by Marcus.Wassmer

	Disable old AMD driver hacks for DX11.  QA has already tested with them off and given thumbs up.

Change 3062241 on 2016/07/22 by Daniel.Wright

	Fixed bug in RHISupportsSeparateMSAAAndResolveTextures that was preventing MSAA for any non-Vulkan platforms

Change 3062244 on 2016/07/22 by Daniel.Wright

	Discard old prenormalized reflection environment data on load

Change 3062283 on 2016/07/22 by Daniel.Wright

	MSAA support for the forward renderer
	* AntiAliasing method is chosen in Rendering project settings, DefaultSettings category
	* Deferred passes like shadow projection, fogging and decals are only computed per-pixel and can introduce aliasing
	* Added Rendering project setting VertexFoggingForOpaque, which makes height fog cheaper and work properly with MSAA
	* The AntiAliasing method in PostProcessSettings has been removed, this may affect existing content
	* Added r.MSAACount which defaults to 4
	* Integrated wide custom resolve filter from Oculus renderer, controlled by r.WideCustomResolve
	* GBuffer targets are no longer allocated when using the forward renderer
	* Decal blend modes that write to the GBuffer fall back to SceneColor emissive only

Change 3062666 on 2016/07/23 by Uriel.Doyon

	Added legend to streaming accuracy viewmodes
	Added a new helper class FRenderTargetTemp to be reused in different canvas rendering.
	Exposed the pass through pixel shader so that it can be reused.
	#review-3058986 @marcus.wassmer

Change 3063023 on 2016/07/25 by Luke.Thatcher

	Fix "RecompileShaders Changed" when using Cook On The Fly.
	#jira UE-33573

Change 3063078 on 2016/07/25 by Ben.Woodhouse

	Add -emitdrawevents command line option to emit draw events by default. This is useful when capturing with Renderdoc

Change 3063315 on 2016/07/25 by Ben.Woodhouse

	Fix div 0 in motion blur. This caused artifacts in some fairly common cases
	#jira UE-32331

Change 3063897 on 2016/07/25 by Uriel.Doyon

	Fixed missing qualifier on interpolants

Change 3064559 on 2016/07/26 by Ben.Woodhouse

	Fix for cooker crash with BC6H textures (XB1, but may affect other platforms). Also fixes corruption issue with texture slices not being a multiple of 4 pixels (expanding as necessary), courtesy of Stu McKenna at the Coalition
	Tested fix on xbox, PC and PS4, using QAGame
	#jira UE-28592

Change 3064896 on 2016/07/26 by Ben.Woodhouse

	Fix compile errors on PS4 (the variable "sample" was conflicting with a keyword, causing compile errors). Also making encoding consistent on new shaders (ansi rather than UTF16)

Change 3064913 on 2016/07/26 by Ben.Marsh

	Fix spelling of "Editor, Tools, Monolithics & DDC" node in Dev-Rendering build settings.

Change 3065326 on 2016/07/26 by Uriel.Doyon

	Fixed UnbuiltInstanceBoundsList not being reset correctly, creating broken rendered primitives.
	#jira UE-32585

Change 3065541 on 2016/07/26 by Daniel.Wright

	Materials with a GBuffer SceneTexture lookup will fail to compile with forward shading

Change 3065543 on 2016/07/26 by Daniel.Wright

	Restored DetailMode changes causing a FGlobalComponentRecreateRenderStateContext - accidental removal from cl 2969413

Change 3065545 on 2016/07/26 by Daniel.Wright

	Added material property bNormalCurvatureToRoughness, which can slightly reduce aliasing.  Tweakable impact with r.NormalCurvatureToRoughnessScale.
	Fixed reflection capture feedback with base pass reflections

Change 3066783 on 2016/07/27 by Daniel.Wright

	Moved PreShadowCacheDepthZ out of FSceneRenderTargets and into FScene, which fixes issues with cached preshadows and multiple scenes, including HighResScreenShot
	Disabled GMinScreenRadiusForShadowCaster on per-object shadows, which fixes popping when trying to increase shadow resolution from the defaults (r.Shadow.TexelsPerPixel 3)

Change 3066794 on 2016/07/27 by Daniel.Wright

	Fixed crash rendering planar reflections due to NULL PostProcessSettings

Change 3067412 on 2016/07/27 by Daniel.Wright

	Fix for OpenGL4 with uint interpolator

Change 3068470 on 2016/07/28 by Daniel.Wright

	Fixed crash rendering translucency with translucent shadows which were determined to be invisible

Change 3069046 on 2016/07/28 by Daniel.Wright

	Handle null Family in SetupAntiAliasingMethod

Change 3069059 on 2016/07/28 by Daniel.Wright

	Added r.ReflectionEnvironmentBeginMixingRoughness (.1) and r.ReflectionEnvironmentEndMixingRoughness (.3), which can be used to tweak the lightmap mixing heuristc, or revert to previous behavior (mixing even on a mirror surface)

Change 3069391 on 2016/07/28 by Daniel.Wright

	Fixed AverageBrightness being applied to reflections in gamma space in the mobile base pass, causing ES2 reflections to be overbright

Change 3070369 on 2016/07/29 by Daniel.Wright

	r.ReflectionEnvironmentBeginMixingRoughness and r.ReflectionEnvironmentEndMixingRoughness set to 0 can be used to achieve old non-roughness based lightmap mixing

Change 3070370 on 2016/07/29 by Daniel.Wright

	Bumped reflection capture DDC version to get rid of legacy prenormalized data

Change 3070680 on 2016/07/29 by Marcus.Wassmer

	Fix slate ensure that is most likely a timing issue exposed by rendering.
	#ue-33902

Change 3070811 on 2016/07/29 by Marcus.Wassmer

	Fix ProjectLauncher errors when loading old versions
	#ue-33939

Change 3070971 on 2016/07/29 by Uriel.Doyon

	Updated ListTextures outputs to fix cooked VS non cooked differences and also to put enphasis on disk VS memory

Change 3071452 on 2016/07/31 by Uriel.Doyon

	Updated the legend description for the (texture streaming) primitive distance accuracy view mode

[CL 3072803 by Marcus Wassmer in Main branch]
2016-08-01 18:56:49 -04:00

1280 lines
48 KiB
C++

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
/*=============================================================================
TranslucentRendering.cpp: Translucent rendering implementation.
=============================================================================*/
#include "RendererPrivate.h"
#include "ScenePrivate.h"
#include "ScreenRendering.h"
#include "SceneFilterRendering.h"
#include "LightPropagationVolume.h"
#include "SceneUtils.h"
DECLARE_CYCLE_STAT(TEXT("TranslucencyTimestampQueryFence Wait"), STAT_TranslucencyTimestampQueryFence_Wait, STATGROUP_SceneRendering);
DECLARE_CYCLE_STAT(TEXT("TranslucencyTimestampQuery Wait"), STAT_TranslucencyTimestampQuery_Wait, STATGROUP_SceneRendering);
DECLARE_FLOAT_COUNTER_STAT(TEXT("Translucency GPU Time (MS)"), STAT_TranslucencyGPU, STATGROUP_SceneRendering);
DECLARE_FLOAT_COUNTER_STAT(TEXT("Translucency"), Stat_GPU_Translucency, STATGROUP_GPU);
static TAutoConsoleVariable<int32> CVarSeparateTranslucencyAutoDownsample(
TEXT("r.SeparateTranslucencyAutoDownsample"),
0,
TEXT("Whether to automatically downsample separate translucency based on last frame's GPU time.\n")
TEXT("Automatic downsampling is only used when r.SeparateTranslucencyScreenPercentage is 100"),
ECVF_Scalability | ECVF_Default);
static TAutoConsoleVariable<float> CVarSeparateTranslucencyDurationDownsampleThreshold(
TEXT("r.SeparateTranslucencyDurationDownsampleThreshold"),
1.5f,
TEXT("When smoothed full-res translucency GPU duration is larger than this value (ms), the entire pass will be downsampled by a factor of 2 in each dimension."),
ECVF_Scalability | ECVF_Default);
static TAutoConsoleVariable<float> CVarSeparateTranslucencyDurationUpsampleThreshold(
TEXT("r.SeparateTranslucencyDurationUpsampleThreshold"),
.5f,
TEXT("When smoothed half-res translucency GPU duration is smaller than this value (ms), the entire pass will be restored to full resolution.\n")
TEXT("This should be around 1/4 of r.SeparateTranslucencyDurationDownsampleThreshold to avoid toggling downsampled state constantly."),
ECVF_Scalability | ECVF_Default);
static TAutoConsoleVariable<float> CVarSeparateTranslucencyMinDownsampleChangeTime(
TEXT("r.SeparateTranslucencyMinDownsampleChangeTime"),
1.0f,
TEXT("Minimum time in seconds between changes to automatic downsampling state, used to prevent rapid swapping between half and full res."),
ECVF_Scalability | ECVF_Default);
void FDeferredShadingSceneRenderer::UpdateTranslucencyTimersAndSeparateTranslucencyBufferSize(FRHICommandListImmediate& RHICmdList)
{
bool bAnyViewWantsDownsampledSeparateTranslucency = false;
bool bCVarSeparateTranslucencyAutoDownsample = CVarSeparateTranslucencyAutoDownsample.GetValueOnRenderThread() != 0;
#if (!STATS)
if (bCVarSeparateTranslucencyAutoDownsample)
#endif
{
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
FSceneViewState* ViewState = View.ViewState;
if (ViewState)
{
//We always tick the separate trans timer but only need the other timer for stats
bool bSeparateTransTimerSuccess = ViewState->SeparateTranslucencyTimer.Tick(RHICmdList);
if (STATS)
{
ViewState->TranslucencyTimer.Tick(RHICmdList);
//Stats are fed the most recent available time and so are lagged a little.
float MostRecentTotalTime = ViewState->TranslucencyTimer.GetTimeMS() + ViewState->SeparateTranslucencyTimer.GetTimeMS();
SET_FLOAT_STAT(STAT_TranslucencyGPU, MostRecentTotalTime);
}
if (bCVarSeparateTranslucencyAutoDownsample && bSeparateTransTimerSuccess)
{
float LastFrameTranslucencyDurationMS = ViewState->SeparateTranslucencyTimer.GetTimeMS();
const bool bOriginalShouldAutoDownsampleTranslucency = ViewState->bShouldAutoDownsampleTranslucency;
if (ViewState->bShouldAutoDownsampleTranslucency)
{
ViewState->SmoothedFullResTranslucencyGPUDuration = 0;
const float LerpAlpha = ViewState->SmoothedHalfResTranslucencyGPUDuration == 0 ? 1.0f : .1f;
ViewState->SmoothedHalfResTranslucencyGPUDuration = FMath::Lerp(ViewState->SmoothedHalfResTranslucencyGPUDuration, LastFrameTranslucencyDurationMS, LerpAlpha);
// Don't re-asses switching for some time after the last switch
if (View.Family->CurrentRealTime - ViewState->LastAutoDownsampleChangeTime > CVarSeparateTranslucencyMinDownsampleChangeTime.GetValueOnRenderThread())
{
// Downsample if the smoothed time is larger than the threshold
ViewState->bShouldAutoDownsampleTranslucency = ViewState->SmoothedHalfResTranslucencyGPUDuration > CVarSeparateTranslucencyDurationUpsampleThreshold.GetValueOnRenderThread();
if (!ViewState->bShouldAutoDownsampleTranslucency)
{
// Do 'log LogRenderer verbose' to get these
UE_LOG(LogRenderer, Verbose, TEXT("Upsample: %.1fms < %.1fms"), ViewState->SmoothedHalfResTranslucencyGPUDuration, CVarSeparateTranslucencyDurationUpsampleThreshold.GetValueOnRenderThread());
}
}
}
else
{
ViewState->SmoothedHalfResTranslucencyGPUDuration = 0;
const float LerpAlpha = ViewState->SmoothedFullResTranslucencyGPUDuration == 0 ? 1.0f : .1f;
ViewState->SmoothedFullResTranslucencyGPUDuration = FMath::Lerp(ViewState->SmoothedFullResTranslucencyGPUDuration, LastFrameTranslucencyDurationMS, LerpAlpha);
if (View.Family->CurrentRealTime - ViewState->LastAutoDownsampleChangeTime > CVarSeparateTranslucencyMinDownsampleChangeTime.GetValueOnRenderThread())
{
// Downsample if the smoothed time is larger than the threshold
ViewState->bShouldAutoDownsampleTranslucency = ViewState->SmoothedFullResTranslucencyGPUDuration > CVarSeparateTranslucencyDurationDownsampleThreshold.GetValueOnRenderThread();
if (ViewState->bShouldAutoDownsampleTranslucency)
{
UE_LOG(LogRenderer, Verbose, TEXT("Downsample: %.1fms > %.1fms"), ViewState->SmoothedFullResTranslucencyGPUDuration, CVarSeparateTranslucencyDurationDownsampleThreshold.GetValueOnRenderThread());
}
}
}
if (bOriginalShouldAutoDownsampleTranslucency != ViewState->bShouldAutoDownsampleTranslucency)
{
ViewState->LastAutoDownsampleChangeTime = View.Family->CurrentRealTime;
}
bAnyViewWantsDownsampledSeparateTranslucency = bAnyViewWantsDownsampledSeparateTranslucency || ViewState->bShouldAutoDownsampleTranslucency;
}
}
}
}
FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList);
SceneContext.SetSeparateTranslucencyBufferSize(bAnyViewWantsDownsampledSeparateTranslucency);
}
void FDeferredShadingSceneRenderer::BeginTimingSeparateTranslucencyPass(FRHICommandListImmediate& RHICmdList, const FViewInfo& View)
{
if (View.ViewState
&& GSupportsTimestampRenderQueries
#if !STATS
&& (CVarSeparateTranslucencyAutoDownsample.GetValueOnRenderThread() != 0)
#endif
)
{
View.ViewState->SeparateTranslucencyTimer.Begin(RHICmdList);
}
}
void FDeferredShadingSceneRenderer::EndTimingSeparateTranslucencyPass(FRHICommandListImmediate& RHICmdList, const FViewInfo& View)
{
if (View.ViewState
&& GSupportsTimestampRenderQueries
#if !STATS
&& (CVarSeparateTranslucencyAutoDownsample.GetValueOnRenderThread() != 0)
#endif
)
{
View.ViewState->SeparateTranslucencyTimer.End(RHICmdList);
}
}
static void SetTranslucentRenderTargetAndState(FRHICommandList& RHICmdList, const FViewInfo& View, ETranslucencyPass::Type TranslucencyPass, bool bFirstTimeThisFrame = false)
{
FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList);
bool bSetupTranslucentState = true;
bool bNeedsClear = (&View == View.Family->Views[0]) && bFirstTimeThisFrame;
if ((TranslucencyPass == ETranslucencyPass::TPT_SeparateTransluceny) && SceneContext.IsSeparateTranslucencyActive(View))
{
bSetupTranslucentState = SceneContext.BeginRenderingSeparateTranslucency(RHICmdList, View, bNeedsClear);
}
else if (TranslucencyPass == ETranslucencyPass::TPT_NonSeparateTransluceny)
{
SceneContext.BeginRenderingTranslucency(RHICmdList, View, bNeedsClear);
}
if (bSetupTranslucentState)
{
// Enable depth test, disable depth writes.
RHICmdList.SetDepthStencilState(TStaticDepthStencilState<false, CF_DepthNearOrEqual>::GetRHI());
}
}
static void FinishTranslucentRenderTarget(FRHICommandListImmediate& RHICmdList, const FViewInfo& View, ETranslucencyPass::Type TranslucencyPass)
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_FinishTranslucentRenderTarget);
FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList);
if ((TranslucencyPass == ETranslucencyPass::TPT_SeparateTransluceny) && SceneContext.IsSeparateTranslucencyActive(View))
{
SceneContext.FinishRenderingSeparateTranslucency(RHICmdList, View);
}
else
{
SceneContext.FinishRenderingTranslucency(RHICmdList, View);
}
}
const FProjectedShadowInfo* FDeferredShadingSceneRenderer::PrepareTranslucentShadowMap(FRHICommandList& RHICmdList, const FViewInfo& View, FPrimitiveSceneInfo* PrimitiveSceneInfo, ETranslucencyPass::Type TranslucencyPass)
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_PrepareTranslucentShadowMap);
const FVisibleLightInfo* VisibleLightInfo = NULL;
FProjectedShadowInfo* TranslucentSelfShadow = NULL;
// Find this primitive's self shadow if there is one
if (PrimitiveSceneInfo->Proxy && PrimitiveSceneInfo->Proxy->CastsVolumetricTranslucentShadow())
{
for (FLightPrimitiveInteraction* Interaction = PrimitiveSceneInfo->LightList;
Interaction && !TranslucentSelfShadow;
Interaction = Interaction->GetNextLight()
)
{
const FLightSceneInfo* LightSceneInfo = Interaction->GetLight();
// Note: applying shadowmap from first directional light found
if (LightSceneInfo->Proxy->GetLightType() == LightType_Directional)
{
VisibleLightInfo = &VisibleLightInfos[LightSceneInfo->Id];
for (int32 ShadowIndex = 0, Count = VisibleLightInfo->AllProjectedShadows.Num(); ShadowIndex < Count; ShadowIndex++)
{
FProjectedShadowInfo* CurrentShadowInfo = VisibleLightInfo->AllProjectedShadows[ShadowIndex];
if (CurrentShadowInfo
&& CurrentShadowInfo->bTranslucentShadow
&& CurrentShadowInfo->GetParentSceneInfo() == PrimitiveSceneInfo
&& CurrentShadowInfo->ShadowDepthView)
{
check(CurrentShadowInfo->RenderTargets.ColorTargets.Num() > 0);
TranslucentSelfShadow = CurrentShadowInfo;
break;
}
}
}
}
}
return TranslucentSelfShadow;
}
/** Pixel shader used to copy scene color into another texture so that materials can read from scene color with a node. */
class FCopySceneColorPS : public FGlobalShader
{
DECLARE_SHADER_TYPE(FCopySceneColorPS,Global);
public:
static bool ShouldCache(EShaderPlatform Platform) { return IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM4); }
FCopySceneColorPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer):
FGlobalShader(Initializer)
{
SceneTextureParameters.Bind(Initializer.ParameterMap);
}
FCopySceneColorPS() {}
void SetParameters(FRHICommandList& RHICmdList, const FViewInfo& View)
{
SceneTextureParameters.Set(RHICmdList, GetPixelShader(), View);
}
virtual bool Serialize(FArchive& Ar) override
{
bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
Ar << SceneTextureParameters;
return bShaderHasOutdatedParameters;
}
private:
FSceneTextureShaderParameters SceneTextureParameters;
};
IMPLEMENT_SHADER_TYPE(,FCopySceneColorPS,TEXT("TranslucentLightingShaders"),TEXT("CopySceneColorMain"),SF_Pixel);
FGlobalBoundShaderState CopySceneColorBoundShaderState;
void FTranslucencyDrawingPolicyFactory::CopySceneColor(FRHICommandList& RHICmdList, const FViewInfo& View, const FPrimitiveSceneProxy* PrimitiveSceneProxy)
{
FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList);
SCOPED_DRAW_EVENTF(RHICmdList, EventCopy, TEXT("CopySceneColor from SceneColor node for %s %s"), *PrimitiveSceneProxy->GetOwnerName().ToString(), *PrimitiveSceneProxy->GetResourceName().ToString());
RHICmdList.SetRasterizerState(TStaticRasterizerState<FM_Solid, CM_None>::GetRHI());
RHICmdList.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always>::GetRHI());
RHICmdList.SetBlendState(TStaticBlendState<>::GetRHI());
SceneContext.ResolveSceneColor(RHICmdList);
SceneContext.BeginRenderingLightAttenuation(RHICmdList);
RHICmdList.SetViewport(View.ViewRect.Min.X, View.ViewRect.Min.Y, 0.0f, View.ViewRect.Max.X, View.ViewRect.Max.Y, 1.0f);
TShaderMapRef<FScreenVS> ScreenVertexShader(View.ShaderMap);
TShaderMapRef<FCopySceneColorPS> PixelShader(View.ShaderMap);
SetGlobalBoundShaderState(RHICmdList, View.GetFeatureLevel(), CopySceneColorBoundShaderState, GFilterVertexDeclaration.VertexDeclarationRHI, *ScreenVertexShader, *PixelShader);
/// ?
PixelShader->SetParameters(RHICmdList, View);
DrawRectangle(
RHICmdList,
0, 0,
View.ViewRect.Width(), View.ViewRect.Height(),
View.ViewRect.Min.X, View.ViewRect.Min.Y,
View.ViewRect.Width(), View.ViewRect.Height(),
FIntPoint(View.ViewRect.Width(), View.ViewRect.Height()),
SceneContext.GetBufferSizeXY(),
*ScreenVertexShader,
EDRF_UseTriangleOptimization);
SceneContext.FinishRenderingLightAttenuation(RHICmdList);
}
/** The parameters used to draw a translucent mesh. */
class FDrawTranslucentMeshAction
{
public:
const FViewInfo& View;
const FProjectedShadowInfo* TranslucentSelfShadow;
FHitProxyId HitProxyId;
bool bBackFace;
FMeshDrawingRenderState DrawRenderState;
bool bUseTranslucentSelfShadowing;
float DownsampleFactorFromSceneBufferSize;
/** Initialization constructor. */
FDrawTranslucentMeshAction(
const FViewInfo& InView,
bool bInBackFace,
const FMeshDrawingRenderState& InDrawRenderState,
FHitProxyId InHitProxyId,
const FProjectedShadowInfo* InTranslucentSelfShadow,
bool bInUseTranslucentSelfShadowing,
float InDownsampleFactorFromSceneBufferSize
) :
View(InView),
TranslucentSelfShadow(InTranslucentSelfShadow),
HitProxyId(InHitProxyId),
bBackFace(bInBackFace),
DrawRenderState(InDrawRenderState),
bUseTranslucentSelfShadowing(bInUseTranslucentSelfShadowing),
DownsampleFactorFromSceneBufferSize(InDownsampleFactorFromSceneBufferSize)
{}
bool UseTranslucentSelfShadowing() const
{
return bUseTranslucentSelfShadowing;
}
const FProjectedShadowInfo* GetTranslucentSelfShadow() const
{
return TranslucentSelfShadow;
}
bool AllowIndirectLightingCache() const
{
const FScene* Scene = (const FScene*)View.Family->Scene;
return View.Family->EngineShowFlags.IndirectLightingCache && Scene && Scene->PrecomputedLightVolumes.Num() > 0;
}
bool AllowIndirectLightingCacheVolumeTexture() const
{
// This will force the cheaper single sample interpolated GI path
return false;
}
/** Draws the translucent mesh with a specific light-map type, and fog volume type */
template<typename LightMapPolicyType>
void Process(
FRHICommandList& RHICmdList,
const FProcessBasePassMeshParameters& Parameters,
const LightMapPolicyType& LightMapPolicy,
const typename LightMapPolicyType::ElementDataType& LightMapElementData
) const
{
const bool bIsLitMaterial = Parameters.ShadingModel != MSM_Unlit;
const FScene* Scene = Parameters.PrimitiveSceneProxy ? Parameters.PrimitiveSceneProxy->GetPrimitiveSceneInfo()->Scene : NULL;
const bool bRenderSkylight = Scene && Scene->ShouldRenderSkylight(Parameters.BlendMode) && bIsLitMaterial;
const bool bRenderAtmosphericFog =(Scene && Scene->HasAtmosphericFog() && Scene->ReadOnlyCVARCache.bEnableAtmosphericFog) && View.Family->EngineShowFlags.AtmosphericFog && View.Family->EngineShowFlags.Fog;
TBasePassDrawingPolicy<LightMapPolicyType> DrawingPolicy(
Parameters.Mesh.VertexFactory,
Parameters.Mesh.MaterialRenderProxy,
*Parameters.Material,
Parameters.FeatureLevel,
LightMapPolicy,
Parameters.BlendMode,
// Translucent meshes need scene render targets set as textures
ESceneRenderTargetsMode::SetTextures,
bRenderSkylight,
bRenderAtmosphericFog,
View.Family->GetDebugViewShaderMode(),
Parameters.bAllowFog,
false,
false);
RHICmdList.BuildAndSetLocalBoundShaderState(DrawingPolicy.GetBoundShaderStateInput(View.GetFeatureLevel()));
DrawingPolicy.SetSharedState(RHICmdList, &View, typename TBasePassDrawingPolicy<LightMapPolicyType>::ContextDataType(), DownsampleFactorFromSceneBufferSize);
int32 BatchElementIndex = 0;
uint64 BatchElementMask = Parameters.BatchElementMask;
do
{
if(BatchElementMask & 1)
{
TDrawEvent<FRHICommandList> MeshEvent;
BeginMeshDrawEvent(RHICmdList, Parameters.PrimitiveSceneProxy, Parameters.Mesh, MeshEvent);
DrawingPolicy.SetMeshRenderState(
RHICmdList,
View,
Parameters.PrimitiveSceneProxy,
Parameters.Mesh,
BatchElementIndex,
bBackFace,
DrawRenderState,
typename TBasePassDrawingPolicy<LightMapPolicyType>::ElementDataType(LightMapElementData),
typename TBasePassDrawingPolicy<LightMapPolicyType>::ContextDataType()
);
DrawingPolicy.DrawMesh(RHICmdList, Parameters.Mesh,BatchElementIndex);
}
BatchElementMask >>= 1;
BatchElementIndex++;
} while(BatchElementMask);
}
};
static void CopySceneColorAndRestore(FRHICommandList& RHICmdList, const FViewInfo& View, const FPrimitiveSceneProxy* PrimitiveSceneProxy)
{
check(IsInRenderingThread());
FTranslucencyDrawingPolicyFactory::CopySceneColor(RHICmdList, View, PrimitiveSceneProxy);
// Restore state
SetTranslucentRenderTargetAndState(RHICmdList, View, ETranslucencyPass::TPT_NonSeparateTransluceny);
}
class FCopySceneColorAndRestoreRenderThreadTask
{
FRHICommandList& RHICmdList;
const FViewInfo& View;
const FPrimitiveSceneProxy* PrimitiveSceneProxy;
public:
FCopySceneColorAndRestoreRenderThreadTask(FRHICommandList& InRHICmdList, const FViewInfo& InView, const FPrimitiveSceneProxy* InPrimitiveSceneProxy)
: RHICmdList(InRHICmdList)
, View(InView)
, PrimitiveSceneProxy(InPrimitiveSceneProxy)
{
}
FORCEINLINE TStatId GetStatId() const
{
RETURN_QUICK_DECLARE_CYCLE_STAT(FCopySceneColorAndRestoreRenderThreadTask, STATGROUP_TaskGraphTasks);
}
ENamedThreads::Type GetDesiredThread()
{
return ENamedThreads::RenderThread_Local;
}
static ESubsequentsMode::Type GetSubsequentsMode() { return ESubsequentsMode::TrackSubsequents; }
void DoTask(ENamedThreads::Type CurrentThread, const FGraphEventRef& MyCompletionGraphEvent)
{
CopySceneColorAndRestore(RHICmdList, View, PrimitiveSceneProxy);
}
};
/**
* Render a dynamic or static mesh using a translucent draw policy
* @return true if the mesh rendered
*/
bool FTranslucencyDrawingPolicyFactory::DrawMesh(
FRHICommandList& RHICmdList,
const FViewInfo& View,
ContextType DrawingContext,
const FMeshBatch& Mesh,
const uint64& BatchElementMask,
bool bBackFace,
const FMeshDrawingRenderState& DrawRenderState,
bool bPreFog,
const FPrimitiveSceneProxy* PrimitiveSceneProxy,
FHitProxyId HitProxyId,
bool bSeparateTranslucencyEnabled
)
{
bool bDirty = false;
const auto FeatureLevel = View.GetFeatureLevel();
// Determine the mesh's material and blend mode.
const FMaterial* Material = Mesh.MaterialRenderProxy->GetMaterial(FeatureLevel);
const EBlendMode BlendMode = Material->GetBlendMode();
// Only render translucent materials
if(IsTranslucentBlendMode(BlendMode))
{
bool bCurrentlyRenderingSeparateTranslucency = Material->IsSeparateTranslucencyEnabled() == (DrawingContext.TranslucenyPassType == ETranslucencyPass::TPT_SeparateTransluceny);
// if we are in relevant pass
if (bCurrentlyRenderingSeparateTranslucency || bSeparateTranslucencyEnabled == false)
{
if (Material->RequiresSceneColorCopy_RenderThread())
{
if (DrawingContext.bSceneColorCopyIsUpToDate == false)
{
if (!RHICmdList.Bypass() && !IsInActualRenderingThread() && !IsInGameThread())
{
FRHICommandList* CmdList = new FRHICommandList;
CmdList->CopyRenderThreadContexts(RHICmdList);
FGraphEventRef RenderThreadCompletionEvent = TGraphTask<FCopySceneColorAndRestoreRenderThreadTask>::CreateTask().ConstructAndDispatchWhenReady(*CmdList, View, PrimitiveSceneProxy);
RHICmdList.QueueRenderThreadCommandListSubmit(RenderThreadCompletionEvent, CmdList);
}
else
{
// otherwise, just do it now. We don't want to defer in this case because that can interfere with render target visualization (a debugging tool).
CopySceneColorAndRestore(RHICmdList, View, PrimitiveSceneProxy);
}
// todo: this optimization is currently broken
DrawingContext.bSceneColorCopyIsUpToDate = (DrawingContext.TranslucenyPassType == ETranslucencyPass::TPT_SeparateTransluceny);
}
}
const bool bDisableDepthTest = Material->ShouldDisableDepthTest();
const bool bEnableResponsiveAA = Material->ShouldEnableResponsiveAA();
// editor compositing not supported on translucent materials currently
const bool bEditorCompositeDepthTest = false;
// if this draw is coming postAA then there is probably no depth buffer (it's canvas) and bEnableResponsiveAA wont' do anything anyway.
if (bEnableResponsiveAA && !DrawingContext.bPostAA)
{
if( bDisableDepthTest )
{
RHICmdList.SetDepthStencilState(TStaticDepthStencilState<false,CF_Always,true,CF_Always,SO_Keep,SO_Keep,SO_Replace>::GetRHI(), 1);
}
else
{
RHICmdList.SetDepthStencilState(TStaticDepthStencilState<false,CF_DepthNearOrEqual,true,CF_Always,SO_Keep,SO_Keep,SO_Replace>::GetRHI(), 1);
}
}
else if( bDisableDepthTest )
{
RHICmdList.SetDepthStencilState(TStaticDepthStencilState<false,CF_Always>::GetRHI());
}
FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList);
FIntPoint OutScaledSize;
float OutScale;
SceneContext.GetSeparateTranslucencyDimensions(OutScaledSize, OutScale);
ProcessBasePassMesh(
RHICmdList,
FProcessBasePassMeshParameters(
Mesh,
BatchElementMask,
Material,
PrimitiveSceneProxy,
!bPreFog,
bEditorCompositeDepthTest,
ESceneRenderTargetsMode::SetTextures,
FeatureLevel
),
FDrawTranslucentMeshAction(
View,
bBackFace,
DrawRenderState,
HitProxyId,
DrawingContext.TranslucentSelfShadow,
PrimitiveSceneProxy && PrimitiveSceneProxy->CastsVolumetricTranslucentShadow(),
bCurrentlyRenderingSeparateTranslucency ? 1.0f / OutScale : 1.0f
)
);
if (bDisableDepthTest || bEnableResponsiveAA)
{
// Restore default depth state
RHICmdList.SetDepthStencilState(TStaticDepthStencilState<false,CF_DepthNearOrEqual>::GetRHI());
}
bDirty = true;
}
}
return bDirty;
}
/**
* Render a dynamic mesh using a translucent draw policy
* @return true if the mesh rendered
*/
bool FTranslucencyDrawingPolicyFactory::DrawDynamicMesh(
FRHICommandList& RHICmdList,
const FViewInfo& View,
ContextType DrawingContext,
const FMeshBatch& Mesh,
bool bBackFace,
bool bPreFog,
const FPrimitiveSceneProxy* PrimitiveSceneProxy,
FHitProxyId HitProxyId,
bool bSeparateTranslucencyEnabled
)
{
return DrawMesh(
RHICmdList,
View,
DrawingContext,
Mesh,
Mesh.Elements.Num() == 1 ? 1 : (1 << Mesh.Elements.Num()) - 1, // 1 bit set for each mesh element
bBackFace,
FMeshDrawingRenderState(Mesh.DitheredLODTransitionAlpha),
bPreFog,
PrimitiveSceneProxy,
HitProxyId,
bSeparateTranslucencyEnabled
);
}
/**
* Render a static mesh using a translucent draw policy
* @return true if the mesh rendered
*/
bool FTranslucencyDrawingPolicyFactory::DrawStaticMesh(
FRHICommandList& RHICmdList,
const FViewInfo& View,
ContextType DrawingContext,
const FStaticMesh& StaticMesh,
const uint64& BatchElementMask,
bool bPreFog,
const FPrimitiveSceneProxy* PrimitiveSceneProxy,
FHitProxyId HitProxyId,
bool bSeparateTranslucencyEnabled
)
{
const FMeshDrawingRenderState DrawRenderState(View.GetDitheredLODTransitionState(StaticMesh));
return DrawMesh(
RHICmdList,
View,
DrawingContext,
StaticMesh,
BatchElementMask,
false,
DrawRenderState,
bPreFog,
PrimitiveSceneProxy,
HitProxyId,
bSeparateTranslucencyEnabled
);
}
/*-----------------------------------------------------------------------------
FTranslucentPrimSet
-----------------------------------------------------------------------------*/
void FTranslucentPrimSet::DrawAPrimitive(
FRHICommandList& RHICmdList,
const FViewInfo& View,
FDeferredShadingSceneRenderer& Renderer,
ETranslucencyPass::Type TranslucencyPass,
int32 PrimIdx
) const
{
FPrimitiveSceneInfo* PrimitiveSceneInfo = SortedPrims[PrimIdx].PrimitiveSceneInfo;
int32 PrimitiveId = PrimitiveSceneInfo->GetIndex();
const FPrimitiveViewRelevance& ViewRelevance = View.PrimitiveViewRelevanceMap[PrimitiveId];
checkSlow(ViewRelevance.HasTranslucency());
const FProjectedShadowInfo* TranslucentSelfShadow = Renderer.PrepareTranslucentShadowMap(RHICmdList, View, PrimitiveSceneInfo, TranslucencyPass);
RenderPrimitive(RHICmdList, View, PrimitiveSceneInfo, ViewRelevance, TranslucentSelfShadow, TranslucencyPass);
}
class FVolumetricTranslucentShadowRenderThreadTask
{
FRHICommandList& RHICmdList;
const FTranslucentPrimSet &PrimSet;
const FViewInfo& View;
FDeferredShadingSceneRenderer& Renderer;
ETranslucencyPass::Type TranslucenyPassType;
int32 Index;
public:
FORCEINLINE_DEBUGGABLE FVolumetricTranslucentShadowRenderThreadTask(FRHICommandList& InRHICmdList, const FTranslucentPrimSet& InPrimSet, const FViewInfo& InView, FDeferredShadingSceneRenderer& InRenderer, ETranslucencyPass::Type InTranslucenyPassType, int32 InIndex)
: RHICmdList(InRHICmdList)
, PrimSet(InPrimSet)
, View(InView)
, Renderer(InRenderer)
, TranslucenyPassType(InTranslucenyPassType)
, Index(InIndex)
{
}
FORCEINLINE TStatId GetStatId() const
{
RETURN_QUICK_DECLARE_CYCLE_STAT(FVolumetricTranslucentShadowRenderThreadTask, STATGROUP_TaskGraphTasks);
}
ENamedThreads::Type GetDesiredThread()
{
return ENamedThreads::RenderThread_Local;
}
static ESubsequentsMode::Type GetSubsequentsMode() { return ESubsequentsMode::TrackSubsequents; }
void DoTask(ENamedThreads::Type CurrentThread, const FGraphEventRef& MyCompletionGraphEvent)
{
PrimSet.DrawAPrimitive(RHICmdList, View, Renderer, TranslucenyPassType, Index);
}
};
void FTranslucentPrimSet::DrawPrimitivesParallel(
FRHICommandList& RHICmdList,
const FViewInfo& View,
FDeferredShadingSceneRenderer& Renderer,
ETranslucencyPass::Type TranslucenyPassType,
int32 FirstPrimIdx, int32 LastPrimIdx
) const
{
// Draw sorted scene prims
for (int32 PrimIdx = FirstPrimIdx; PrimIdx <= LastPrimIdx; PrimIdx++)
{
FPrimitiveSceneInfo* PrimitiveSceneInfo = SortedPrims[PrimIdx].PrimitiveSceneInfo;
int32 PrimitiveId = PrimitiveSceneInfo->GetIndex();
const FPrimitiveViewRelevance& ViewRelevance = View.PrimitiveViewRelevanceMap[PrimitiveId];
checkSlow(ViewRelevance.HasTranslucency());
if (PrimitiveSceneInfo->Proxy && PrimitiveSceneInfo->Proxy->CastsVolumetricTranslucentShadow())
{
check(!IsInActualRenderingThread());
// can't do this in parallel, defer
FRHICommandList* CmdList = new FRHICommandList;
CmdList->CopyRenderThreadContexts(RHICmdList);
FGraphEventRef RenderThreadCompletionEvent = TGraphTask<FVolumetricTranslucentShadowRenderThreadTask>::CreateTask().ConstructAndDispatchWhenReady(*CmdList, *this, View, Renderer, TranslucenyPassType, PrimIdx);
RHICmdList.QueueRenderThreadCommandListSubmit(RenderThreadCompletionEvent, CmdList);
}
else
{
RenderPrimitive(RHICmdList, View, PrimitiveSceneInfo, ViewRelevance, nullptr, TranslucenyPassType);
}
}
}
void FTranslucentPrimSet::DrawPrimitives(
FRHICommandListImmediate& RHICmdList,
const FViewInfo& View,
FDeferredShadingSceneRenderer& Renderer,
ETranslucencyPass::Type TranslucenyPassType
) const
{
FInt32Range PassRange = SortedPrimsNum.GetPassRange(TranslucenyPassType);
// Draw sorted scene prims
for( int32 PrimIdx = PassRange.GetLowerBoundValue(); PrimIdx < PassRange.GetUpperBoundValue(); PrimIdx++ )
{
FPrimitiveSceneInfo* PrimitiveSceneInfo = SortedPrims[PrimIdx].PrimitiveSceneInfo;
int32 PrimitiveId = PrimitiveSceneInfo->GetIndex();
const FPrimitiveViewRelevance& ViewRelevance = View.PrimitiveViewRelevanceMap[PrimitiveId];
checkSlow(ViewRelevance.HasTranslucency());
const FProjectedShadowInfo* TranslucentSelfShadow = Renderer.PrepareTranslucentShadowMap(RHICmdList, View, PrimitiveSceneInfo, TranslucenyPassType);
RenderPrimitive(RHICmdList, View, PrimitiveSceneInfo, ViewRelevance, TranslucentSelfShadow, TranslucenyPassType);
}
View.SimpleElementCollector.DrawBatchedElements(RHICmdList, View, FTexture2DRHIRef(), EBlendModeFilter::Translucent);
}
void FTranslucentPrimSet::RenderPrimitive(
FRHICommandList& RHICmdList,
const FViewInfo& View,
FPrimitiveSceneInfo* PrimitiveSceneInfo,
const FPrimitiveViewRelevance& ViewRelevance,
const FProjectedShadowInfo* TranslucentSelfShadow,
ETranslucencyPass::Type TranslucenyPassType) const
{
checkSlow(ViewRelevance.HasTranslucency());
auto FeatureLevel = View.GetFeatureLevel();
if (ViewRelevance.bDrawRelevance)
{
FTranslucencyDrawingPolicyFactory::ContextType Context(TranslucentSelfShadow, TranslucenyPassType);
// need to check further down if we can skip rendering ST primitives, because we need to make sure they render in the normal translucency pass otherwise
// getting the cvar here and passing it down to be more efficient
bool bSeparateTranslucencyPossible = (FSceneRenderTargets::CVarSetSeperateTranslucencyEnabled.GetValueOnRenderThread() != 0) && View.Family->EngineShowFlags.SeparateTranslucency && View.Family->EngineShowFlags.PostProcessing;
// Render dynamic scene prim
{
// range in View.DynamicMeshElements[]
FInt32Range range = View.GetDynamicMeshElementRange(PrimitiveSceneInfo->GetIndex());
for (int32 MeshBatchIndex = range.GetLowerBoundValue(); MeshBatchIndex < range.GetUpperBoundValue(); MeshBatchIndex++)
{
const FMeshBatchAndRelevance& MeshBatchAndRelevance = View.DynamicMeshElements[MeshBatchIndex];
checkSlow(MeshBatchAndRelevance.PrimitiveSceneProxy == PrimitiveSceneInfo->Proxy);
const FMeshBatch& MeshBatch = *MeshBatchAndRelevance.Mesh;
FTranslucencyDrawingPolicyFactory::DrawDynamicMesh(RHICmdList, View, Context, MeshBatch, false, false, MeshBatchAndRelevance.PrimitiveSceneProxy, MeshBatch.BatchHitProxyId, bSeparateTranslucencyPossible);
}
}
// Render static scene prim
if (ViewRelevance.bStaticRelevance)
{
// Render static meshes from static scene prim
for (int32 StaticMeshIdx = 0, Count = PrimitiveSceneInfo->StaticMeshes.Num(); StaticMeshIdx < Count; StaticMeshIdx++)
{
FStaticMesh& StaticMesh = PrimitiveSceneInfo->StaticMeshes[StaticMeshIdx];
bool bMaterialMatchesPass = (StaticMesh.MaterialRenderProxy->GetMaterial(FeatureLevel)->IsSeparateTranslucencyEnabled() == (TranslucenyPassType == ETranslucencyPass::TPT_SeparateTransluceny));
bool bShouldRenderMesh = bMaterialMatchesPass || (!bSeparateTranslucencyPossible);
if (View.StaticMeshVisibilityMap[StaticMesh.Id]
// Only render static mesh elements using translucent materials
&& StaticMesh.IsTranslucent(FeatureLevel)
&& bShouldRenderMesh )
{
FTranslucencyDrawingPolicyFactory::DrawStaticMesh(
RHICmdList,
View,
FTranslucencyDrawingPolicyFactory::ContextType(TranslucentSelfShadow, TranslucenyPassType),
StaticMesh,
StaticMesh.bRequiresPerElementVisibility ? View.StaticMeshBatchVisibility[StaticMesh.Id] : ((1ull << StaticMesh.Elements.Num()) - 1),
false,
PrimitiveSceneInfo->Proxy,
StaticMesh.BatchHitProxyId,
bSeparateTranslucencyPossible
);
}
}
}
}
}
inline float CalculateTranslucentSortKey(FPrimitiveSceneInfo* PrimitiveSceneInfo, const FViewInfo& ViewInfo)
{
float SortKey = 0.0f;
if (ViewInfo.TranslucentSortPolicy == ETranslucentSortPolicy::SortByDistance)
{
//sort based on distance to the view position, view rotation is not a factor
SortKey = (PrimitiveSceneInfo->Proxy->GetBounds().Origin - ViewInfo.ViewMatrices.ViewOrigin).Size();
// UE4_TODO: also account for DPG in the sort key.
}
else if (ViewInfo.TranslucentSortPolicy == ETranslucentSortPolicy::SortAlongAxis)
{
// Sort based on enforced orthogonal distance
const FVector CameraToObject = PrimitiveSceneInfo->Proxy->GetBounds().Origin - ViewInfo.ViewMatrices.ViewOrigin;
SortKey = FVector::DotProduct(CameraToObject, ViewInfo.TranslucentSortAxis);
}
else
{
// Sort based on projected Z distance
check(ViewInfo.TranslucentSortPolicy == ETranslucentSortPolicy::SortByProjectedZ);
SortKey = ViewInfo.ViewMatrices.ViewMatrix.TransformPosition(PrimitiveSceneInfo->Proxy->GetBounds().Origin).Z;
}
return SortKey;
}
void FTranslucentPrimSet::AppendScenePrimitives(FTranslucentSortedPrim* Elements, int32 Num, const FTranslucenyPrimCount& TranslucentPrimitiveCountPerPass)
{
SortedPrims.Append(Elements, Num);
SortedPrimsNum.Append(TranslucentPrimitiveCountPerPass);
}
void FTranslucentPrimSet::PlaceScenePrimitive(FPrimitiveSceneInfo* PrimitiveSceneInfo, const FViewInfo& ViewInfo, bool bUseNormalTranslucency, bool bUseSeparateTranslucency, bool bUseMobileSeparateTranslucency,
FTranslucentPrimSet::FTranslucentSortedPrim *InArrayStart, int32& InOutArrayNum, FTranslucenyPrimCount& OutCount)
{
const float SortKey = CalculateTranslucentSortKey(PrimitiveSceneInfo, ViewInfo);
const auto FeatureLevel = ViewInfo.GetFeatureLevel();
int32 CVarEnabled = FSceneRenderTargets::CVarSetSeperateTranslucencyEnabled.GetValueOnRenderThread();
bool bCanBeSeparate = CVarEnabled
&& FeatureLevel >= ERHIFeatureLevel::SM4
&& ViewInfo.Family->EngineShowFlags.PostProcessing
&& !ViewInfo.Family->EngineShowFlags.ShaderComplexity
&& ViewInfo.Family->EngineShowFlags.SeparateTranslucency;
bool bIsSeparateTranslucency = bUseSeparateTranslucency && bCanBeSeparate;
bool bIsNonSeparateTranslucency = bUseNormalTranslucency || !bCanBeSeparate;
if (bIsSeparateTranslucency)
{
ETranslucencyPass::Type TranslucencyPass = ETranslucencyPass::TPT_SeparateTransluceny;
new(&InArrayStart[InOutArrayNum++]) FTranslucentSortedPrim(PrimitiveSceneInfo, TranslucencyPass, PrimitiveSceneInfo->Proxy->GetTranslucencySortPriority(), SortKey);
OutCount.Add(TranslucencyPass);
}
if (bIsNonSeparateTranslucency)
{
ETranslucencyPass::Type TranslucencyPass = ETranslucencyPass::TPT_NonSeparateTransluceny;
new(&InArrayStart[InOutArrayNum++]) FTranslucentSortedPrim(PrimitiveSceneInfo, TranslucencyPass, PrimitiveSceneInfo->Proxy->GetTranslucencySortPriority(), SortKey);
OutCount.Add(TranslucencyPass);
}
}
void FTranslucentPrimSet::SortPrimitives()
{
// sort prims based on the specified criteria (usually depth)
SortedPrims.Sort( FCompareFTranslucentSortedPrim() );
}
bool FSceneRenderer::ShouldRenderTranslucency() const
{
bool bRender = false;
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
if (View.TranslucentPrimSet.NumPrims() || View.bHasTranslucentViewMeshElements)
{
bRender = true;
break;
}
}
return bRender;
}
class FDrawSortedTransAnyThreadTask : public FRenderTask
{
FDeferredShadingSceneRenderer& Renderer;
FRHICommandList& RHICmdList;
const FViewInfo& View;
ETranslucencyPass::Type TranslucenyPassType;
const int32 FirstIndex;
const int32 LastIndex;
public:
FDrawSortedTransAnyThreadTask(
FDeferredShadingSceneRenderer& InRenderer,
FRHICommandList& InRHICmdList,
const FViewInfo& InView,
ETranslucencyPass::Type InTranslucenyPassType,
int32 InFirstIndex,
int32 InLastIndex
)
: Renderer(InRenderer)
, RHICmdList(InRHICmdList)
, View(InView)
, TranslucenyPassType(InTranslucenyPassType)
, FirstIndex(InFirstIndex)
, LastIndex(InLastIndex)
{
}
FORCEINLINE TStatId GetStatId() const
{
RETURN_QUICK_DECLARE_CYCLE_STAT(FDrawSortedTransAnyThreadTask, STATGROUP_TaskGraphTasks);
}
static ESubsequentsMode::Type GetSubsequentsMode() { return ESubsequentsMode::TrackSubsequents; }
void DoTask(ENamedThreads::Type CurrentThread, const FGraphEventRef& MyCompletionGraphEvent)
{
FScopeCycleCounter ScopeOuter(RHICmdList.ExecuteStat);
View.TranslucentPrimSet.DrawPrimitivesParallel(RHICmdList, View, Renderer, TranslucenyPassType, FirstIndex, LastIndex);
RHICmdList.HandleRTThreadTaskCompletion(MyCompletionGraphEvent);
}
};
DECLARE_CYCLE_STAT(TEXT("Translucency"), STAT_CLP_Translucency, STATGROUP_ParallelCommandListMarkers);
class FTranslucencyPassParallelCommandListSet : public FParallelCommandListSet
{
ETranslucencyPass::Type TranslucenyPassType;
bool bFirstTimeThisFrame;
public:
FTranslucencyPassParallelCommandListSet(const FViewInfo& InView, FRHICommandListImmediate& InParentCmdList, bool bInParallelExecute, bool bInCreateSceneContext, ETranslucencyPass::Type InTranslucenyPassType)
: FParallelCommandListSet(GET_STATID(STAT_CLP_Translucency), InView, InParentCmdList, bInParallelExecute, bInCreateSceneContext)
, TranslucenyPassType(InTranslucenyPassType)
, bFirstTimeThisFrame(true)
{
SetStateOnCommandList(ParentCmdList);
}
virtual ~FTranslucencyPassParallelCommandListSet()
{
Dispatch();
}
virtual void SetStateOnCommandList(FRHICommandList& CmdList) override
{
SetTranslucentRenderTargetAndState(CmdList, View, TranslucenyPassType, bFirstTimeThisFrame);
bFirstTimeThisFrame = false;
}
};
static TAutoConsoleVariable<int32> CVarRHICmdTranslucencyPassDeferredContexts(
TEXT("r.RHICmdTranslucencyPassDeferredContexts"),
1,
TEXT("True to use deferred contexts to parallelize base pass command list execution."));
static TAutoConsoleVariable<int32> CVarRHICmdFlushRenderThreadTasksTranslucentPass(
TEXT("r.RHICmdFlushRenderThreadTasksTranslucentPass"),
0,
TEXT("Wait for completion of parallel render thread tasks at the end of the translucent pass. A more granular version of r.RHICmdFlushRenderThreadTasks. If either r.RHICmdFlushRenderThreadTasks or r.RHICmdFlushRenderThreadTasksTranslucentPass is > 0 we will flush."));
// this is a static because we let the async tasks neyond the function
static FTranslucencyDrawingPolicyFactory::ContextType GParallelTranslucencyContext;
void FDeferredShadingSceneRenderer::RenderTranslucencyParallel(FRHICommandListImmediate& RHICmdList)
{
FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList);
SceneContext.AllocLightAttenuation(RHICmdList); // materials will attempt to get this texture before the deferred command to set it up executes
check(IsInRenderingThread());
GParallelTranslucencyContext.TranslucentSelfShadow = nullptr;
GParallelTranslucencyContext.TranslucenyPassType = ETranslucencyPass::TPT_NonSeparateTransluceny;
GParallelTranslucencyContext.bSceneColorCopyIsUpToDate = false;
FScopedCommandListWaitForTasks Flusher(CVarRHICmdFlushRenderThreadTasksTranslucentPass.GetValueOnRenderThread() > 0 || CVarRHICmdFlushRenderThreadTasks.GetValueOnRenderThread() > 0, RHICmdList);
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
SCOPED_CONDITIONAL_DRAW_EVENTF(RHICmdList, EventView, Views.Num() > 1, TEXT("View%d"), ViewIndex);
const FViewInfo& View = Views[ViewIndex];
{
if (SceneContext.IsSeparateTranslucencyActive(View))
{
QUICK_SCOPE_CYCLE_COUNTER(RenderTranslucencyParallel_Downsample);
// we need to allocate this now so it ends up in the snapshot
FIntPoint ScaledSize;
float Scale = 1.0f;
SceneContext.GetSeparateTranslucencyDimensions(ScaledSize, Scale);
if (Scale<1.0f)
{
SceneContext.GetSeparateTranslucencyDepth(RHICmdList, SceneContext.GetBufferSizeXY());
DownsampleDepthSurface(RHICmdList, SceneContext.GetSeparateTranslucencyDepthSurface(), View, Scale, false);
}
}
#if STATS
if (View.ViewState)
{
View.ViewState->TranslucencyTimer.Begin(RHICmdList);
}
#endif
ETranslucencyPass::Type TranslucenyPassType = ETranslucencyPass::TPT_NonSeparateTransluceny;
FTranslucencyPassParallelCommandListSet ParallelCommandListSet(View, RHICmdList,
CVarRHICmdTranslucencyPassDeferredContexts.GetValueOnRenderThread() > 0,
CVarRHICmdFlushRenderThreadTasksTranslucentPass.GetValueOnRenderThread() == 0 && CVarRHICmdFlushRenderThreadTasks.GetValueOnRenderThread() == 0,
TranslucenyPassType);
{
QUICK_SCOPE_CYCLE_COUNTER(RenderTranslucencyParallel_Start_FDrawSortedTransAnyThreadTask);
FInt32Range PassRange = View.TranslucentPrimSet.SortedPrimsNum.GetPassRange(TranslucenyPassType);
int32 NumPrims = PassRange.Size<int32>();
int32 EffectiveThreads = FMath::Min<int32>(FMath::DivideAndRoundUp(NumPrims, ParallelCommandListSet.MinDrawsPerCommandList), ParallelCommandListSet.Width);
int32 Start = PassRange.GetLowerBoundValue();
if (EffectiveThreads)
{
int32 NumPer = NumPrims / EffectiveThreads;
int32 Extra = NumPrims - NumPer * EffectiveThreads;
for (int32 ThreadIndex = 0; ThreadIndex < EffectiveThreads; ThreadIndex++)
{
int32 Last = Start + (NumPer - 1) + (ThreadIndex < Extra);
check(Last >= Start);
{
FRHICommandList* CmdList = ParallelCommandListSet.NewParallelCommandList();
FGraphEventRef AnyThreadCompletionEvent = TGraphTask<FDrawSortedTransAnyThreadTask>::CreateTask(ParallelCommandListSet.GetPrereqs(), ENamedThreads::RenderThread)
.ConstructAndDispatchWhenReady(*this, *CmdList, View, TranslucenyPassType, Start, Last);
ParallelCommandListSet.AddParallelCommandList(CmdList, AnyThreadCompletionEvent);
}
Start = Last + 1;
}
}
}
// Draw the view's mesh elements with the translucent drawing policy.
{
QUICK_SCOPE_CYCLE_COUNTER(RenderTranslucencyParallel_SDPG_World);
DrawViewElementsParallel<FTranslucencyDrawingPolicyFactory>(GParallelTranslucencyContext, SDPG_World, false, ParallelCommandListSet);
}
// Draw the view's mesh elements with the translucent drawing policy.
{
QUICK_SCOPE_CYCLE_COUNTER(RenderTranslucencyParallel_SDPG_Foreground);
DrawViewElementsParallel<FTranslucencyDrawingPolicyFactory>(GParallelTranslucencyContext, SDPG_Foreground, false, ParallelCommandListSet);
}
FinishTranslucentRenderTarget(RHICmdList, View, TranslucenyPassType);
}
#if STATS
if (View.ViewState)
{
View.ViewState->TranslucencyTimer.End(RHICmdList);
}
#endif
#if 0 // unsupported visualization in the parallel case
const FSceneViewState* ViewState = (const FSceneViewState*)View.State;
if (ViewState && View.Family->EngineShowFlags.VisualizeLPV)
{
FLightPropagationVolume* LightPropagationVolume = ViewState->GetLightPropagationVolume();
if (LightPropagationVolume)
{
LightPropagationVolume->Visualise(RHICmdList, View);
}
}
#endif
{
BeginTimingSeparateTranslucencyPass(RHICmdList, View);
{
ETranslucencyPass::Type TranslucencyPass = ETranslucencyPass::TPT_SeparateTransluceny;
// always call BeginRenderingSeparateTranslucency() even if there are no primitives to we keep the RT allocated
FTranslucencyPassParallelCommandListSet ParallelCommandListSet(View,
RHICmdList,
CVarRHICmdTranslucencyPassDeferredContexts.GetValueOnRenderThread() > 0,
CVarRHICmdFlushRenderThreadTasksTranslucentPass.GetValueOnRenderThread() == 0 && CVarRHICmdFlushRenderThreadTasks.GetValueOnRenderThread() == 0,
TranslucencyPass);
// Draw only translucent prims that are in the SeparateTranslucency pass
if (View.TranslucentPrimSet.SortedPrimsNum.Num(TranslucencyPass) > 0)
{
QUICK_SCOPE_CYCLE_COUNTER(RenderTranslucencyParallel_Start_FDrawSortedTransAnyThreadTask_SeparateTransluceny);
FInt32Range PassRange = View.TranslucentPrimSet.SortedPrimsNum.GetPassRange(TranslucencyPass);
int32 NumPrims = PassRange.Size<int32>();
int32 EffectiveThreads = FMath::Min<int32>(FMath::DivideAndRoundUp(NumPrims, ParallelCommandListSet.MinDrawsPerCommandList), ParallelCommandListSet.Width);
int32 Start = PassRange.GetLowerBoundValue();
check(EffectiveThreads);
{
int32 NumPer = NumPrims / EffectiveThreads;
int32 Extra = NumPrims - NumPer * EffectiveThreads;
for (int32 ThreadIndex = 0; ThreadIndex < EffectiveThreads; ThreadIndex++)
{
int32 Last = Start + (NumPer - 1) + (ThreadIndex < Extra);
check(Last >= Start);
{
FRHICommandList* CmdList = ParallelCommandListSet.NewParallelCommandList();
FGraphEventRef AnyThreadCompletionEvent = TGraphTask<FDrawSortedTransAnyThreadTask>::CreateTask(ParallelCommandListSet.GetPrereqs(), ENamedThreads::RenderThread)
.ConstructAndDispatchWhenReady(*this, *CmdList, View, TranslucencyPass, Start, Last);
ParallelCommandListSet.AddParallelCommandList(CmdList, AnyThreadCompletionEvent);
}
Start = Last + 1;
}
}
}
}
SceneContext.FinishRenderingSeparateTranslucency(RHICmdList, View);
EndTimingSeparateTranslucencyPass(RHICmdList, View);
}
}
}
static TAutoConsoleVariable<int32> CVarParallelTranslucency(
TEXT("r.ParallelTranslucency"),
1,
TEXT("Toggles parallel translucency rendering. Parallel rendering must be enabled for this to have an effect."),
ECVF_RenderThreadSafe
);
void FDeferredShadingSceneRenderer::DrawAllTranslucencyPasses(FRHICommandListImmediate& RHICmdList, const FViewInfo& View, ETranslucencyPass::Type TranslucencyPass)
{
// Draw translucent prims
View.TranslucentPrimSet.DrawPrimitives(RHICmdList, View, *this, TranslucencyPass);
FTranslucencyDrawingPolicyFactory::ContextType Context(0, TranslucencyPass);
// editor and debug rendering
DrawViewElements<FTranslucencyDrawingPolicyFactory>(RHICmdList, View, Context, SDPG_World, false);
DrawViewElements<FTranslucencyDrawingPolicyFactory>(RHICmdList, View, Context, SDPG_Foreground, false);
}
void FDeferredShadingSceneRenderer::RenderTranslucency(FRHICommandListImmediate& RHICmdList)
{
if (ShouldRenderTranslucency())
{
SCOPED_DRAW_EVENT(RHICmdList, Translucency);
SCOPED_GPU_STAT(RHICmdList, Stat_GPU_Translucency);
if (GRHICommandList.UseParallelAlgorithms() && CVarParallelTranslucency.GetValueOnRenderThread())
{
RenderTranslucencyParallel(RHICmdList);
return;
}
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
SCOPED_CONDITIONAL_DRAW_EVENTF(RHICmdList, EventView, Views.Num() > 1, TEXT("View%d"), ViewIndex);
const FViewInfo& View = Views[ViewIndex];
// non separate translucency
{
#if STATS
if (View.ViewState)
{
View.ViewState->TranslucencyTimer.Begin(RHICmdList);
}
#endif
bool bFirstTimeThisFrame = (ViewIndex == 0);
SetTranslucentRenderTargetAndState(RHICmdList, View, ETranslucencyPass::TPT_NonSeparateTransluceny, bFirstTimeThisFrame);
DrawAllTranslucencyPasses(RHICmdList, View, ETranslucencyPass::TPT_NonSeparateTransluceny);
const FSceneViewState* ViewState = (const FSceneViewState*)View.State;
if (ViewState && View.Family->EngineShowFlags.VisualizeLPV)
{
FLightPropagationVolume* LightPropagationVolume = ViewState->GetLightPropagationVolume(View.GetFeatureLevel());
if (LightPropagationVolume)
{
LightPropagationVolume->Visualise(RHICmdList, View);
}
}
FinishTranslucentRenderTarget(RHICmdList, View, ETranslucencyPass::TPT_NonSeparateTransluceny);
#if STATS
if (View.ViewState)
{
View.ViewState->TranslucencyTimer.End(RHICmdList);
}
#endif
}
// separate translucency
{
FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList);
if (SceneContext.IsSeparateTranslucencyActive(View))
{
// always call BeginRenderingSeparateTranslucency() even if there are no primitives to we keep the RT allocated
FIntPoint ScaledSize;
float Scale = 1.0f;
SceneContext.GetSeparateTranslucencyDimensions(ScaledSize, Scale);
if (Scale < 1.0f)
{
SceneContext.GetSeparateTranslucencyDepth(RHICmdList, SceneContext.GetBufferSizeXY());
DownsampleDepthSurface(RHICmdList, SceneContext.GetSeparateTranslucencyDepthSurface(), View, Scale, false);
}
BeginTimingSeparateTranslucencyPass(RHICmdList, View);
bool bFirstTimeThisFrame = (ViewIndex == 0);
bool bSetupTranslucency = SceneContext.BeginRenderingSeparateTranslucency(RHICmdList, View, bFirstTimeThisFrame);
const TIndirectArray<FMeshBatch>& WorldList = View.ViewMeshElements;
const TIndirectArray<FMeshBatch>& ForegroundList = View.TopViewMeshElements;
bool bRenderSeparateTranslucency = View.TranslucentPrimSet.SortedPrimsNum.Num(ETranslucencyPass::TPT_SeparateTransluceny) > 0 || WorldList.Num() || ForegroundList.Num();
// Draw only translucent prims that are in the SeparateTranslucency pass
if (bRenderSeparateTranslucency)
{
if (bSetupTranslucency)
{
RHICmdList.SetDepthStencilState(TStaticDepthStencilState<false, CF_DepthNearOrEqual>::GetRHI());
}
DrawAllTranslucencyPasses(RHICmdList, View, ETranslucencyPass::TPT_SeparateTransluceny);
}
SceneContext.FinishRenderingSeparateTranslucency(RHICmdList, View);
EndTimingSeparateTranslucencyPass(RHICmdList, View);
}
}
}
}
}