You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#rb ben.woodhouse #ROBOMERGE-OWNER: ryan.vance #ROBOMERGE-AUTHOR: jeff.newquist #ROBOMERGE-SOURCE: CL 5830660 via CL 5830670 via CL 5835167 via CL 5835925 #ROBOMERGE-BOT: DEVVR (Main -> Dev-VR) [CL 5876639 by jeff newquist in Dev-VR branch]
724 lines
28 KiB
C++
724 lines
28 KiB
C++
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
VelocityRendering.cpp: Velocity rendering implementation.
|
|
=============================================================================*/
|
|
|
|
#include "VelocityRendering.h"
|
|
#include "SceneUtils.h"
|
|
#include "Materials/Material.h"
|
|
#include "PostProcess/SceneRenderTargets.h"
|
|
#include "MaterialShaderType.h"
|
|
#include "MaterialShader.h"
|
|
#include "MeshMaterialShader.h"
|
|
#include "ShaderBaseClasses.h"
|
|
#include "SceneRendering.h"
|
|
#include "DeferredShadingRenderer.h"
|
|
#include "ScenePrivate.h"
|
|
#include "PostProcess/ScreenSpaceReflections.h"
|
|
#include "UnrealEngine.h"
|
|
#if WITH_EDITOR
|
|
#include "Misc/CoreMisc.h"
|
|
#include "Interfaces/ITargetPlatform.h"
|
|
#include "Interfaces/ITargetPlatformManagerModule.h"
|
|
#endif
|
|
#include "VisualizeTexture.h"
|
|
#include "MeshPassProcessor.inl"
|
|
|
|
// Changing this causes a full shader recompile
|
|
static TAutoConsoleVariable<int32> CVarBasePassOutputsVelocity(
|
|
TEXT("r.BasePassOutputsVelocity"),
|
|
0,
|
|
TEXT("Enables rendering WPO velocities on the base pass.\n") \
|
|
TEXT(" 0: Renders in a separate pass/rendertarget, all movable static meshes + dynamic.\n") \
|
|
TEXT(" 1: Renders during the regular base pass adding an extra GBuffer, but allowing motion blur on materials with Time-based WPO."),
|
|
ECVF_ReadOnly | ECVF_RenderThreadSafe);
|
|
|
|
static TAutoConsoleVariable<int32> CVarParallelVelocity(
|
|
TEXT("r.ParallelVelocity"),
|
|
1,
|
|
TEXT("Toggles parallel velocity rendering. Parallel rendering must be enabled for this to have an effect."),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarRHICmdVelocityPassDeferredContexts(
|
|
TEXT("r.RHICmdVelocityPassDeferredContexts"),
|
|
1,
|
|
TEXT("True to use deferred contexts to parallelize velocity pass command list execution."));
|
|
|
|
RENDERER_API TAutoConsoleVariable<int32> CVarAllowMotionBlurInVR(
|
|
TEXT("vr.AllowMotionBlurInVR"),
|
|
0,
|
|
TEXT("For projects with motion blur enabled, this allows motion blur to be enabled even while in VR."));
|
|
|
|
DECLARE_GPU_STAT_NAMED(RenderVelocities, TEXT("Render Velocities"));
|
|
|
|
bool IsParallelVelocity()
|
|
{
|
|
return GRHICommandList.UseParallelAlgorithms() && CVarParallelVelocity.GetValueOnRenderThread();
|
|
}
|
|
|
|
//=============================================================================
|
|
/** Encapsulates the Velocity vertex shader. */
|
|
class FVelocityVS : public FMeshMaterialShader
|
|
{
|
|
DECLARE_SHADER_TYPE(FVelocityVS,MeshMaterial);
|
|
|
|
public:
|
|
|
|
bool SupportsVelocity() const
|
|
{
|
|
return GPUSkinCachePreviousPositionBuffer.IsBound() ||
|
|
PrevTransformBuffer.IsBound() ||
|
|
(PrevTransform0.IsBound() && PrevTransform1.IsBound() && PrevTransform2.IsBound()) ||
|
|
//@todo MeshCommandPipeline - now that PreviousLocalToWorld is in the primitive uniform buffer, we can't look at whether the shader bound it to cull what gets rendered in velocity pass
|
|
true;
|
|
}
|
|
|
|
static bool ShouldCompilePermutation(EShaderPlatform Platform,const FMaterial* Material,const FVertexFactoryType* VertexFactoryType)
|
|
{
|
|
//Only compile the velocity shaders for the default material or if it's masked,
|
|
return ((Material->IsSpecialEngineMaterial() || !Material->WritesEveryPixel()
|
|
//or if the material is opaque and two-sided,
|
|
|| (Material->IsTwoSided() && !IsTranslucentBlendMode(Material->GetBlendMode()))
|
|
// or if the material modifies meshes
|
|
|| Material->MaterialMayModifyMeshPosition()))
|
|
&& IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM4)
|
|
&& !FVelocityRendering::VertexFactoryOnlyOutputsVelocityInBasePass(Platform, VertexFactoryType->SupportsStaticLighting());
|
|
}
|
|
|
|
protected:
|
|
FVelocityVS(const ShaderMetaType::CompiledShaderInitializerType& Initializer):
|
|
FMeshMaterialShader(Initializer)
|
|
{
|
|
GPUSkinCachePreviousPositionBuffer.Bind(Initializer.ParameterMap, TEXT("GPUSkinCachePreviousPositionBuffer"));
|
|
PrevTransform0.Bind(Initializer.ParameterMap, TEXT("PrevTransform0"));
|
|
PrevTransform1.Bind(Initializer.ParameterMap, TEXT("PrevTransform1"));
|
|
PrevTransform2.Bind(Initializer.ParameterMap, TEXT("PrevTransform2"));
|
|
PrevTransformBuffer.Bind(Initializer.ParameterMap, TEXT("PrevTransformBuffer"));
|
|
PassUniformBuffer.Bind(Initializer.ParameterMap, FSceneTexturesUniformParameters::StaticStructMetadata.GetShaderVariableName());
|
|
}
|
|
|
|
FVelocityVS() {}
|
|
|
|
virtual bool Serialize(FArchive& Ar) override
|
|
{
|
|
bool bShaderHasOutdatedParameters = FMeshMaterialShader::Serialize(Ar);
|
|
|
|
Ar << GPUSkinCachePreviousPositionBuffer;
|
|
Ar << PrevTransform0;
|
|
Ar << PrevTransform1;
|
|
Ar << PrevTransform2;
|
|
Ar << PrevTransformBuffer;
|
|
|
|
return bShaderHasOutdatedParameters;
|
|
}
|
|
|
|
private:
|
|
FShaderResourceParameter GPUSkinCachePreviousPositionBuffer;
|
|
FShaderParameter PrevTransform0;
|
|
FShaderParameter PrevTransform1;
|
|
FShaderParameter PrevTransform2;
|
|
FShaderResourceParameter PrevTransformBuffer;
|
|
};
|
|
|
|
|
|
//=============================================================================
|
|
/** Encapsulates the Velocity hull shader. */
|
|
class FVelocityHS : public FBaseHS
|
|
{
|
|
DECLARE_SHADER_TYPE(FVelocityHS,MeshMaterial);
|
|
|
|
protected:
|
|
FVelocityHS(const ShaderMetaType::CompiledShaderInitializerType& Initializer):
|
|
FBaseHS(Initializer)
|
|
{}
|
|
|
|
FVelocityHS() {}
|
|
|
|
static bool ShouldCompilePermutation(EShaderPlatform Platform,const FMaterial* Material,const FVertexFactoryType* VertexFactoryType)
|
|
{
|
|
return FBaseHS::ShouldCompilePermutation(Platform, Material, VertexFactoryType) &&
|
|
FVelocityVS::ShouldCompilePermutation(Platform, Material, VertexFactoryType); // same rules as VS
|
|
}
|
|
};
|
|
|
|
//=============================================================================
|
|
/** Encapsulates the Velocity domain shader. */
|
|
class FVelocityDS : public FBaseDS
|
|
{
|
|
DECLARE_SHADER_TYPE(FVelocityDS,MeshMaterial);
|
|
|
|
protected:
|
|
FVelocityDS(const ShaderMetaType::CompiledShaderInitializerType& Initializer):
|
|
FBaseDS(Initializer)
|
|
{}
|
|
|
|
FVelocityDS() {}
|
|
|
|
static bool ShouldCompilePermutation(EShaderPlatform Platform, const FMaterial* Material, const FVertexFactoryType* VertexFactoryType)
|
|
{
|
|
return FBaseDS::ShouldCompilePermutation(Platform, Material, VertexFactoryType) &&
|
|
FVelocityVS::ShouldCompilePermutation(Platform, Material, VertexFactoryType); // same rules as VS
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_MATERIAL_SHADER_TYPE(,FVelocityVS,TEXT("/Engine/Private/VelocityShader.usf"),TEXT("MainVertexShader"),SF_Vertex);
|
|
IMPLEMENT_MATERIAL_SHADER_TYPE(,FVelocityHS,TEXT("/Engine/Private/VelocityShader.usf"),TEXT("MainHull"),SF_Hull);
|
|
IMPLEMENT_MATERIAL_SHADER_TYPE(,FVelocityDS,TEXT("/Engine/Private/VelocityShader.usf"),TEXT("MainDomain"),SF_Domain);
|
|
|
|
//=============================================================================
|
|
/** Encapsulates the Velocity pixel shader. */
|
|
class FVelocityPS : public FMeshMaterialShader
|
|
{
|
|
DECLARE_SHADER_TYPE(FVelocityPS,MeshMaterial);
|
|
public:
|
|
static bool ShouldCompilePermutation(EShaderPlatform Platform,const FMaterial* Material,const FVertexFactoryType* VertexFactoryType)
|
|
{
|
|
//Only compile the velocity shaders for the default material or if it's masked,
|
|
return ((Material->IsSpecialEngineMaterial() || !Material->WritesEveryPixel()
|
|
//or if the material is opaque and two-sided,
|
|
|| (Material->IsTwoSided() && !IsTranslucentBlendMode(Material->GetBlendMode()))
|
|
// or if the material modifies meshes
|
|
|| Material->MaterialMayModifyMeshPosition()))
|
|
&& IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM4) &&
|
|
!FVelocityRendering::VertexFactoryOnlyOutputsVelocityInBasePass(Platform, VertexFactoryType->SupportsStaticLighting());
|
|
}
|
|
|
|
static void ModifyCompilationEnvironment( EShaderPlatform Platform, const FMaterial* Material, FShaderCompilerEnvironment& OutEnvironment )
|
|
{
|
|
FMaterialShader::ModifyCompilationEnvironment(Platform, Material, OutEnvironment);
|
|
OutEnvironment.SetRenderTargetOutputFormat(0, PF_G16R16);
|
|
}
|
|
|
|
FVelocityPS() {}
|
|
|
|
FVelocityPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
|
|
: FMeshMaterialShader(Initializer)
|
|
{
|
|
PassUniformBuffer.Bind(Initializer.ParameterMap, FSceneTexturesUniformParameters::StaticStructMetadata.GetShaderVariableName());
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_MATERIAL_SHADER_TYPE(,FVelocityPS,TEXT("/Engine/Private/VelocityShader.usf"),TEXT("MainPixelShader"),SF_Pixel);
|
|
|
|
IMPLEMENT_SHADERPIPELINE_TYPE_VSPS(VelocityPipeline, FVelocityVS, FVelocityPS, true);
|
|
|
|
|
|
int32 GetMotionBlurQualityFromCVar()
|
|
{
|
|
int32 MotionBlurQuality;
|
|
|
|
static const auto ICVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.MotionBlurQuality"));
|
|
MotionBlurQuality = FMath::Clamp(ICVar->GetValueOnRenderThread(), 0, 4);
|
|
|
|
return MotionBlurQuality;
|
|
}
|
|
|
|
bool IsMotionBlurEnabled(const FViewInfo& View)
|
|
{
|
|
if (View.GetFeatureLevel() < ERHIFeatureLevel::SM5)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int32 MotionBlurQuality = GetMotionBlurQualityFromCVar();
|
|
|
|
return View.Family->EngineShowFlags.PostProcessing
|
|
&& View.Family->EngineShowFlags.MotionBlur
|
|
&& View.FinalPostProcessSettings.MotionBlurAmount > 0.001f
|
|
&& View.FinalPostProcessSettings.MotionBlurMax > 0.001f
|
|
&& View.Family->bRealtimeUpdate
|
|
&& MotionBlurQuality > 0
|
|
&& !IsSimpleForwardShadingEnabled(GShaderPlatformForFeatureLevel[View.GetFeatureLevel()])
|
|
&& (CVarAllowMotionBlurInVR->GetInt() != 0 || !(View.Family->Views.Num() > 1));
|
|
}
|
|
|
|
static void BeginVelocityRendering(FRHICommandList& RHICmdList, TRefCountPtr<IPooledRenderTarget>& VelocityRT, bool bPerformClear)
|
|
{
|
|
check(RHICmdList.IsOutsideRenderPass());
|
|
|
|
FTextureRHIRef VelocityTexture = VelocityRT->GetRenderTargetItem().TargetableTexture;
|
|
FTexture2DRHIRef DepthTexture = FSceneRenderTargets::Get(RHICmdList).GetSceneDepthTexture();
|
|
|
|
FRHIRenderPassInfo RPInfo(VelocityTexture, ERenderTargetActions::Load_Store);
|
|
RPInfo.DepthStencilRenderTarget.Action = MakeDepthStencilTargetActions(ERenderTargetActions::Load_Store, ERenderTargetActions::Load_Store);
|
|
RPInfo.DepthStencilRenderTarget.DepthStencilTarget = DepthTexture;
|
|
RPInfo.DepthStencilRenderTarget.ExclusiveDepthStencil = FExclusiveDepthStencil::DepthRead_StencilWrite;
|
|
|
|
if (bPerformClear)
|
|
{
|
|
RPInfo.ColorRenderTargets[0].Action = ERenderTargetActions::Clear_Store;
|
|
}
|
|
|
|
RHICmdList.BeginRenderPass(RPInfo, TEXT("VelocityRendering"));
|
|
|
|
if (!bPerformClear)
|
|
{
|
|
// some platforms need the clear color when rendertargets transition to SRVs. We propagate here to allow parallel rendering to always
|
|
// have the proper mapping when the RT is transitioned.
|
|
RHICmdList.BindClearMRTValues(true, false, false);
|
|
}
|
|
}
|
|
|
|
static void SetVelocitiesState(FRHICommandList& RHICmdList, const FViewInfo& View, const FSceneRenderer* SceneRender, FMeshPassProcessorRenderState& DrawRenderState, TRefCountPtr<IPooledRenderTarget>& VelocityRT)
|
|
{
|
|
const FIntPoint BufferSize = FSceneRenderTargets::Get(RHICmdList).GetBufferSizeXY();
|
|
const FIntPoint VelocityBufferSize = BufferSize; // full resolution so we can reuse the existing full res z buffer
|
|
|
|
if (!View.IsInstancedStereoPass())
|
|
{
|
|
const uint32 MinX = View.ViewRect.Min.X * VelocityBufferSize.X / BufferSize.X;
|
|
const uint32 MinY = View.ViewRect.Min.Y * VelocityBufferSize.Y / BufferSize.Y;
|
|
const uint32 MaxX = View.ViewRect.Max.X * VelocityBufferSize.X / BufferSize.X;
|
|
const uint32 MaxY = View.ViewRect.Max.Y * VelocityBufferSize.Y / BufferSize.Y;
|
|
RHICmdList.SetViewport(MinX, MinY, 0.0f, MaxX, MaxY, 1.0f);
|
|
}
|
|
else
|
|
{
|
|
if (View.bIsMultiViewEnabled)
|
|
{
|
|
const uint32 LeftMinX = SceneRender->Views[0].ViewRect.Min.X;
|
|
const uint32 LeftMaxX = SceneRender->Views[0].ViewRect.Max.X;
|
|
const uint32 RightMinX = SceneRender->Views[1].ViewRect.Min.X;
|
|
const uint32 RightMaxX = SceneRender->Views[1].ViewRect.Max.X;
|
|
|
|
const uint32 LeftMaxY = SceneRender->Views[0].ViewRect.Max.Y;
|
|
const uint32 RightMaxY = SceneRender->Views[1].ViewRect.Max.Y;
|
|
|
|
RHICmdList.SetStereoViewport(LeftMinX, RightMinX, 0, 0, 0.0f, LeftMaxX, RightMaxX, LeftMaxY, RightMaxY, 1.0f);
|
|
}
|
|
else
|
|
{
|
|
const uint32 MaxX = SceneRender->InstancedStereoWidth * VelocityBufferSize.X / BufferSize.X;
|
|
const uint32 MaxY = View.ViewRect.Max.Y * VelocityBufferSize.Y / BufferSize.Y;
|
|
RHICmdList.SetViewport(0, 0, 0.0f, MaxX, MaxY, 1.0f);
|
|
}
|
|
}
|
|
|
|
DrawRenderState.SetBlendState(TStaticBlendState<CW_RGBA>::GetRHI());
|
|
DrawRenderState.SetDepthStencilState(TStaticDepthStencilState<false, CF_DepthNearOrEqual>::GetRHI());
|
|
//TODO Where does this state go?
|
|
//RHICmdList.SetRasterizerState(GetStaticRasterizerState<true>(FM_Solid, CM_CW));
|
|
}
|
|
|
|
DECLARE_CYCLE_STAT(TEXT("Velocity"), STAT_CLP_Velocity, STATGROUP_ParallelCommandListMarkers);
|
|
|
|
|
|
class FVelocityPassParallelCommandListSet : public FParallelCommandListSet
|
|
{
|
|
TRefCountPtr<IPooledRenderTarget>& VelocityRT;
|
|
|
|
public:
|
|
FVelocityPassParallelCommandListSet(
|
|
const FViewInfo& InView,
|
|
const FSceneRenderer* InSceneRenderer,
|
|
FRHICommandListImmediate& InParentCmdList,
|
|
bool bInParallelExecute,
|
|
bool bInCreateSceneContext,
|
|
const FMeshPassProcessorRenderState& InDrawRenderState,
|
|
TRefCountPtr<IPooledRenderTarget>& InVelocityRT)
|
|
: FParallelCommandListSet(GET_STATID(STAT_CLP_Velocity), InView, InSceneRenderer, InParentCmdList, bInParallelExecute, bInCreateSceneContext, InDrawRenderState)
|
|
, VelocityRT(InVelocityRT)
|
|
{
|
|
}
|
|
|
|
virtual ~FVelocityPassParallelCommandListSet()
|
|
{
|
|
Dispatch();
|
|
}
|
|
|
|
virtual void SetStateOnCommandList(FRHICommandList& CmdList) override
|
|
{
|
|
FParallelCommandListSet::SetStateOnCommandList(CmdList);
|
|
BeginVelocityRendering(CmdList, VelocityRT, false);
|
|
SetVelocitiesState(CmdList, View, SceneRenderer, DrawRenderState, VelocityRT);
|
|
}
|
|
};
|
|
|
|
static TAutoConsoleVariable<int32> CVarRHICmdFlushRenderThreadTasksVelocityPass(
|
|
TEXT("r.RHICmdFlushRenderThreadTasksVelocityPass"),
|
|
0,
|
|
TEXT("Wait for completion of parallel render thread tasks at the end of the velocity pass. A more granular version of r.RHICmdFlushRenderThreadTasks. If either r.RHICmdFlushRenderThreadTasks or r.RHICmdFlushRenderThreadTasksVelocityPass is > 0 we will flush."));
|
|
|
|
void FDeferredShadingSceneRenderer::RenderVelocitiesInnerParallel(FRHICommandListImmediate& RHICmdList, TRefCountPtr<IPooledRenderTarget>& VelocityRT)
|
|
{
|
|
// Parallel rendering requires its own renderpasses so we cannot have an active one at this point
|
|
check(RHICmdList.IsOutsideRenderPass());
|
|
// parallel version
|
|
FScopedCommandListWaitForTasks Flusher(CVarRHICmdFlushRenderThreadTasksVelocityPass.GetValueOnRenderThread() > 0 || CVarRHICmdFlushRenderThreadTasks.GetValueOnRenderThread() > 0, RHICmdList);
|
|
|
|
for(int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
|
|
{
|
|
const FViewInfo& View = Views[ViewIndex];
|
|
|
|
if (View.ShouldRenderView())
|
|
{
|
|
SCOPED_GPU_MASK(RHICmdList, View.GPUMask);
|
|
|
|
Scene->UniformBuffers.UpdateViewUniformBuffer(View);
|
|
|
|
FSceneTexturesUniformParameters SceneTextureParameters;
|
|
FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList);
|
|
SetupSceneTextureUniformParameters(SceneContext, View.FeatureLevel, ESceneTextureSetupMode::None, SceneTextureParameters);
|
|
TUniformBufferRef<FSceneTexturesUniformParameters> PassUniformBuffer = TUniformBufferRef<FSceneTexturesUniformParameters>::CreateUniformBufferImmediate(SceneTextureParameters, UniformBuffer_SingleFrame);
|
|
|
|
FMeshPassProcessorRenderState DrawRenderState(View, PassUniformBuffer);
|
|
|
|
FVelocityPassParallelCommandListSet ParallelCommandListSet(View,
|
|
this,
|
|
RHICmdList,
|
|
CVarRHICmdVelocityPassDeferredContexts.GetValueOnRenderThread() > 0,
|
|
CVarRHICmdFlushRenderThreadTasksVelocityPass.GetValueOnRenderThread() == 0 && CVarRHICmdFlushRenderThreadTasks.GetValueOnRenderThread() == 0,
|
|
DrawRenderState,
|
|
VelocityRT);
|
|
|
|
// Draw velocities.
|
|
View.ParallelMeshDrawCommandPasses[EMeshPass::Velocity].DispatchDraw(&ParallelCommandListSet, RHICmdList);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FDeferredShadingSceneRenderer::RenderVelocitiesInner(FRHICommandListImmediate& RHICmdList, TRefCountPtr<IPooledRenderTarget>& VelocityRT)
|
|
{
|
|
check(RHICmdList.IsInsideRenderPass());
|
|
for(int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
|
|
{
|
|
const FViewInfo& View = Views[ViewIndex];
|
|
|
|
FSceneTexturesUniformParameters SceneTextureParameters;
|
|
FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList);
|
|
SetupSceneTextureUniformParameters(SceneContext, View.FeatureLevel, ESceneTextureSetupMode::None, SceneTextureParameters);
|
|
TUniformBufferRef<FSceneTexturesUniformParameters> PassUniformBuffer = TUniformBufferRef<FSceneTexturesUniformParameters>::CreateUniformBufferImmediate(SceneTextureParameters, UniformBuffer_SingleFrame);
|
|
|
|
FMeshPassProcessorRenderState DrawRenderState(View, PassUniformBuffer);
|
|
|
|
if (View.ShouldRenderView())
|
|
{
|
|
SCOPED_GPU_MASK(RHICmdList, View.GPUMask);
|
|
|
|
Scene->UniformBuffers.UpdateViewUniformBuffer(View);
|
|
|
|
SetVelocitiesState(RHICmdList, View, this, DrawRenderState, VelocityRT);
|
|
|
|
View.ParallelMeshDrawCommandPasses[EMeshPass::Velocity].DispatchDraw(nullptr, RHICmdList);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool FDeferredShadingSceneRenderer::ShouldRenderVelocities() const
|
|
{
|
|
if (!GPixelFormats[PF_G16R16].Supported)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool bNeedsVelocity = false;
|
|
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
|
|
{
|
|
const FViewInfo& View = Views[ViewIndex];
|
|
|
|
bool bTemporalAA = (View.AntiAliasingMethod == AAM_TemporalAA) && !View.bCameraCut;
|
|
bool bMotionBlur = IsMotionBlurEnabled(View);
|
|
bool bDistanceFieldAO = ShouldPrepareForDistanceFieldAO();
|
|
|
|
bool bSSRTemporal = IsSSRTemporalPassRequired(View);
|
|
|
|
bNeedsVelocity |= bMotionBlur || bTemporalAA || bDistanceFieldAO || bSSRTemporal;
|
|
}
|
|
|
|
return bNeedsVelocity;
|
|
}
|
|
|
|
void FDeferredShadingSceneRenderer::RenderVelocities(FRHICommandListImmediate& RHICmdList, TRefCountPtr<IPooledRenderTarget>& VelocityRT)
|
|
{
|
|
SCOPED_NAMED_EVENT(FDeferredShadingSceneRenderer_RenderVelocities, FColor::Emerald);
|
|
|
|
check(FeatureLevel >= ERHIFeatureLevel::SM4);
|
|
CSV_SCOPED_TIMING_STAT_EXCLUSIVE(RenderVelocities);
|
|
SCOPE_CYCLE_COUNTER(STAT_RenderVelocities);
|
|
|
|
if (!ShouldRenderVelocities())
|
|
{
|
|
return;
|
|
}
|
|
|
|
SCOPED_DRAW_EVENT(RHICmdList, RenderVelocities);
|
|
SCOPED_GPU_STAT(RHICmdList, RenderVelocities);
|
|
|
|
FPooledRenderTargetDesc Desc = FVelocityRendering::GetRenderTargetDesc();
|
|
GRenderTargetPool.FindFreeElement(RHICmdList, Desc, VelocityRT, TEXT("Velocity"));
|
|
|
|
{
|
|
static const auto MotionBlurDebugVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.MotionBlurDebug"));
|
|
|
|
if(MotionBlurDebugVar->GetValueOnRenderThread())
|
|
{
|
|
UE_LOG(LogEngine, Log, TEXT("r.MotionBlurDebug: FrameNumber=%d Pause=%d"), ViewFamily.FrameNumber, ViewFamily.bWorldIsPaused ? 1 : 0);
|
|
}
|
|
}
|
|
|
|
{
|
|
// In this case, basepass also outputs some of the velocities, so append is already started, and don't clear the buffer.
|
|
BeginVelocityRendering(RHICmdList, VelocityRT, !FVelocityRendering::BasePassCanOutputVelocity(FeatureLevel));
|
|
}
|
|
|
|
{
|
|
if (IsParallelVelocity())
|
|
{
|
|
// This initial renderpass will just be a clear in the parallel case.
|
|
RHICmdList.EndRenderPass();
|
|
|
|
// Now do parallel encoding.
|
|
RenderVelocitiesInnerParallel(RHICmdList, VelocityRT);
|
|
}
|
|
else
|
|
{
|
|
RenderVelocitiesInner(RHICmdList, VelocityRT);
|
|
RHICmdList.EndRenderPass();
|
|
}
|
|
|
|
RHICmdList.CopyToResolveTarget(VelocityRT->GetRenderTargetItem().TargetableTexture, VelocityRT->GetRenderTargetItem().ShaderResourceTexture, FResolveParams());
|
|
}
|
|
|
|
// to be able to observe results with VisualizeTexture
|
|
GVisualizeTexture.SetCheckPoint(RHICmdList, VelocityRT);
|
|
}
|
|
|
|
FPooledRenderTargetDesc FVelocityRendering::GetRenderTargetDesc()
|
|
{
|
|
const FIntPoint BufferSize = FSceneRenderTargets::Get_FrameConstantsOnly().GetBufferSizeXY();
|
|
const FIntPoint VelocityBufferSize = BufferSize; // full resolution so we can reuse the existing full res z buffer
|
|
return FPooledRenderTargetDesc(FPooledRenderTargetDesc::Create2DDesc(VelocityBufferSize, PF_G16R16, FClearValueBinding::Transparent, TexCreate_None, TexCreate_RenderTargetable, false));
|
|
}
|
|
|
|
bool FVelocityRendering::BasePassCanOutputVelocity(EShaderPlatform ShaderPlatform)
|
|
{
|
|
return IsUsingBasePassVelocity(ShaderPlatform);
|
|
}
|
|
|
|
bool FVelocityRendering::BasePassCanOutputVelocity(ERHIFeatureLevel::Type FeatureLevel)
|
|
{
|
|
EShaderPlatform ShaderPlatform = GetFeatureLevelShaderPlatform(FeatureLevel);
|
|
return BasePassCanOutputVelocity(ShaderPlatform);
|
|
}
|
|
|
|
bool FVelocityRendering::VertexFactoryOnlyOutputsVelocityInBasePass(EShaderPlatform ShaderPlatform, bool bVertexFactorySupportsStaticLighting)
|
|
{
|
|
return BasePassCanOutputVelocity(ShaderPlatform) && !(IsUsingSelectiveBasePassOutputs(ShaderPlatform) && bVertexFactorySupportsStaticLighting);;
|
|
}
|
|
|
|
bool FVelocityRendering::PrimitiveHasVelocity(ERHIFeatureLevel::Type FeatureLevel, const FPrimitiveSceneInfo* PrimitiveSceneInfo)
|
|
{
|
|
// No velocity if motionblur is off, or if it's a non-moving object (treat as background in that case).
|
|
if (!GPixelFormats[PF_G16R16].Supported || !PrimitiveSceneInfo->Proxy->IsMovable())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// If the base pass is allowed to render velocity in the GBuffer, only mesh with static lighting need the velocity pass.
|
|
if (FVelocityRendering::BasePassCanOutputVelocity(FeatureLevel) && !(IsUsingSelectiveBasePassOutputs(GetFeatureLevelShaderPlatform(FeatureLevel)) && PrimitiveSceneInfo->Proxy->HasStaticLighting()))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FVelocityRendering::PrimitiveHasVelocityForView(const FViewInfo& View, const FBoxSphereBounds& Bounds, const FPrimitiveSceneInfo* PrimitiveSceneInfo)
|
|
{
|
|
if (View.bCameraCut)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const float LODFactorDistanceSquared = (Bounds.Origin - View.ViewMatrices.GetViewOrigin()).SizeSquared() * FMath::Square(View.LODDistanceFactor);
|
|
|
|
// The minimum projected screen radius for a primitive to be drawn in the velocity pass, as a fraction of half the horizontal screen width (likely to be 0.08f)
|
|
float MinScreenRadiusForVelocityPass = View.FinalPostProcessSettings.MotionBlurPerObjectSize * (2.0f / 100.0f);
|
|
float MinScreenRadiusForVelocityPassSquared = FMath::Square(MinScreenRadiusForVelocityPass);
|
|
|
|
// Skip primitives that only cover a small amount of screenspace, motion blur on them won't be noticeable.
|
|
if (FMath::Square(Bounds.SphereRadius) <= MinScreenRadiusForVelocityPassSquared * LODFactorDistanceSquared)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (PrimitiveSceneInfo->Proxy->AlwaysHasVelocity())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// check if the primitive has moved
|
|
{
|
|
const FScene* Scene = PrimitiveSceneInfo->Scene;
|
|
|
|
const FMatrix& LocalToWorld = PrimitiveSceneInfo->Proxy->GetLocalToWorld();
|
|
FMatrix PreviousLocalToWorld = LocalToWorld;
|
|
Scene->VelocityData.GetComponentPreviousLocalToWorld(PrimitiveSceneInfo->PrimitiveComponentId, PreviousLocalToWorld);
|
|
|
|
if (LocalToWorld.Equals(PreviousLocalToWorld, 0.0001f))
|
|
{
|
|
// Hasn't moved (treat as background by not rendering any special velocities)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void FVelocityMeshProcessor::AddMeshBatch(const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, int32 StaticMeshId)
|
|
{
|
|
bool bRequiresSeparateVelocity = false;
|
|
|
|
if (FVelocityRendering::PrimitiveHasVelocity(FeatureLevel, PrimitiveSceneProxy->GetPrimitiveSceneInfo()))
|
|
{
|
|
bRequiresSeparateVelocity = true;
|
|
|
|
// Cached mesh commands have identical check inside MarkRelevant.
|
|
if (ViewIfDynamicMeshCommand)
|
|
{
|
|
checkSlow(ViewIfDynamicMeshCommand->bIsViewInfo);
|
|
FViewInfo* ViewInfo = (FViewInfo*)ViewIfDynamicMeshCommand;
|
|
|
|
bRequiresSeparateVelocity = FVelocityRendering::PrimitiveHasVelocityForView(*ViewInfo, PrimitiveSceneProxy->GetBounds(), PrimitiveSceneProxy->GetPrimitiveSceneInfo());
|
|
}
|
|
}
|
|
|
|
if (MeshBatch.bUseForMaterial && bRequiresSeparateVelocity)
|
|
{
|
|
// Determine the mesh's material and blend mode.
|
|
const FMaterialRenderProxy* MaterialRenderProxy = nullptr;
|
|
const FMaterial* Material = &MeshBatch.MaterialRenderProxy->GetMaterialWithFallback(FeatureLevel, MaterialRenderProxy);
|
|
const EBlendMode BlendMode = Material->GetBlendMode();
|
|
const ERasterizerFillMode MeshFillMode = ComputeMeshFillMode(MeshBatch, *Material);
|
|
const ERasterizerCullMode MeshCullMode = ComputeMeshCullMode(MeshBatch, *Material);
|
|
|
|
if (BlendMode == BLEND_Opaque || BlendMode == BLEND_Masked)
|
|
{
|
|
if (Material->WritesEveryPixel() && !Material->IsTwoSided() && !Material->MaterialModifiesMeshPosition_RenderThread())
|
|
{
|
|
// Default material doesn't handle masked or mesh-mod, and doesn't have the correct bIsTwoSided setting.
|
|
MaterialRenderProxy = UMaterial::GetDefaultMaterial(MD_Surface)->GetRenderProxy();
|
|
Material = MaterialRenderProxy->GetMaterial(FeatureLevel);
|
|
}
|
|
|
|
if (!MaterialRenderProxy)
|
|
{
|
|
MaterialRenderProxy = MeshBatch.MaterialRenderProxy;
|
|
}
|
|
|
|
check(Material && MaterialRenderProxy);
|
|
|
|
Process(MeshBatch, BatchElementMask, StaticMeshId, PrimitiveSceneProxy, *MaterialRenderProxy, *Material, MeshFillMode, MeshCullMode);
|
|
}
|
|
}
|
|
}
|
|
|
|
void GetVelocityPassShaders(
|
|
const FMaterial& Material,
|
|
FVertexFactoryType* VertexFactoryType,
|
|
ERHIFeatureLevel::Type FeatureLevel,
|
|
FVelocityHS*& HullShader,
|
|
FVelocityDS*& DomainShader,
|
|
FVelocityVS*& VertexShader,
|
|
FVelocityPS*& PixelShader)
|
|
{
|
|
const EMaterialTessellationMode MaterialTessellationMode = Material.GetTessellationMode();
|
|
|
|
const bool bNeedsHSDS = RHISupportsTessellation(GShaderPlatformForFeatureLevel[FeatureLevel])
|
|
&& VertexFactoryType->SupportsTessellationShaders()
|
|
&& MaterialTessellationMode != MTM_NoTessellation;
|
|
|
|
if (bNeedsHSDS)
|
|
{
|
|
DomainShader = Material.GetShader<FVelocityDS>(VertexFactoryType);
|
|
HullShader = Material.GetShader<FVelocityHS>(VertexFactoryType);
|
|
}
|
|
|
|
static const auto* CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.ShaderPipelines"));
|
|
const bool bUseShaderPipelines = RHISupportsShaderPipelines(GShaderPlatformForFeatureLevel[FeatureLevel]) && !bNeedsHSDS && CVar && CVar->GetValueOnAnyThread() != 0;
|
|
|
|
FShaderPipeline* ShaderPipeline = bUseShaderPipelines ? Material.GetShaderPipeline(&VelocityPipeline, VertexFactoryType, false) : nullptr;
|
|
if (ShaderPipeline)
|
|
{
|
|
VertexShader = ShaderPipeline->GetShader<FVelocityVS>();
|
|
PixelShader = ShaderPipeline->GetShader<FVelocityPS>();
|
|
check(VertexShader && PixelShader);
|
|
}
|
|
else
|
|
{
|
|
VertexShader = Material.GetShader<FVelocityVS>(VertexFactoryType);
|
|
PixelShader = Material.GetShader<FVelocityPS>(VertexFactoryType);
|
|
check(VertexShader && PixelShader);
|
|
}
|
|
}
|
|
|
|
void FVelocityMeshProcessor::Process(
|
|
const FMeshBatch& MeshBatch,
|
|
uint64 BatchElementMask,
|
|
int32 StaticMeshId,
|
|
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
|
|
const FMaterialRenderProxy& RESTRICT MaterialRenderProxy,
|
|
const FMaterial& RESTRICT MaterialResource,
|
|
ERasterizerFillMode MeshFillMode,
|
|
ERasterizerCullMode MeshCullMode)
|
|
{
|
|
const FVertexFactory* VertexFactory = MeshBatch.VertexFactory;
|
|
|
|
TMeshProcessorShaders<
|
|
FVelocityVS,
|
|
FVelocityHS,
|
|
FVelocityDS,
|
|
FVelocityPS> VelocityPassShaders;
|
|
|
|
GetVelocityPassShaders(
|
|
MaterialResource,
|
|
VertexFactory->GetType(),
|
|
FeatureLevel,
|
|
VelocityPassShaders.HullShader,
|
|
VelocityPassShaders.DomainShader,
|
|
VelocityPassShaders.VertexShader,
|
|
VelocityPassShaders.PixelShader
|
|
);
|
|
|
|
FMeshMaterialShaderElementData ShaderElementData;
|
|
ShaderElementData.InitializeMeshMaterialData(ViewIfDynamicMeshCommand, PrimitiveSceneProxy, MeshBatch, StaticMeshId, false);
|
|
|
|
const FMeshDrawCommandSortKey SortKey = CalculateMeshStaticSortKey(VelocityPassShaders.VertexShader, VelocityPassShaders.PixelShader);
|
|
|
|
BuildMeshDrawCommands(
|
|
MeshBatch,
|
|
BatchElementMask,
|
|
PrimitiveSceneProxy,
|
|
MaterialRenderProxy,
|
|
MaterialResource,
|
|
PassDrawRenderState,
|
|
VelocityPassShaders,
|
|
MeshFillMode,
|
|
MeshCullMode,
|
|
SortKey,
|
|
EMeshPassFeatures::Default,
|
|
ShaderElementData);
|
|
}
|
|
|
|
FVelocityMeshProcessor::FVelocityMeshProcessor(const FScene* Scene, const FSceneView* InViewIfDynamicMeshCommand, const FMeshPassProcessorRenderState& InPassDrawRenderState, FMeshPassDrawListContext* InDrawListContext)
|
|
: FMeshPassProcessor(Scene, Scene->GetFeatureLevel(), InViewIfDynamicMeshCommand, InDrawListContext)
|
|
{
|
|
PassDrawRenderState = InPassDrawRenderState;
|
|
PassDrawRenderState.SetViewUniformBuffer(Scene->UniformBuffers.ViewUniformBuffer);
|
|
PassDrawRenderState.SetInstancedViewUniformBuffer(Scene->UniformBuffers.InstancedViewUniformBuffer);
|
|
PassDrawRenderState.SetPassUniformBuffer(Scene->UniformBuffers.VelocityPassUniformBuffer);
|
|
}
|
|
|
|
FMeshPassProcessor* CreateVelocityPassProcessor(const FScene* Scene, const FSceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext)
|
|
{
|
|
FMeshPassProcessorRenderState VelocityPassState;
|
|
VelocityPassState.SetBlendState(TStaticBlendState<CW_RGBA>::GetRHI());
|
|
VelocityPassState.SetDepthStencilState(TStaticDepthStencilState<false, CF_DepthNearOrEqual>::GetRHI());
|
|
|
|
return new(FMemStack::Get()) FVelocityMeshProcessor(Scene, InViewIfDynamicMeshCommand, VelocityPassState, InDrawListContext);
|
|
}
|
|
|
|
FRegisterPassProcessorCreateFunction RegisterVelocityPass(&CreateVelocityPassProcessor, EShadingPath::Deferred, EMeshPass::Velocity, EMeshPassFlags::CachedMeshCommands | EMeshPassFlags::MainView); |