932 lines
37 KiB
C++
932 lines
37 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "VelocityRendering.h"
|
|
#include "DataDrivenShaderPlatformInfo.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 "ScreenSpaceRayTracing.h"
|
|
#include "PostProcess/PostProcessMotionBlur.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"
|
|
#include "DebugProbeRendering.h"
|
|
#include "RendererModule.h"
|
|
#include "RenderCore.h"
|
|
|
|
// Changing this causes a full shader recompile
|
|
static TAutoConsoleVariable<int32> CVarVelocityOutputPass(
|
|
TEXT("r.VelocityOutputPass"),
|
|
0,
|
|
TEXT("When to write velocity buffer.\n") \
|
|
TEXT(" 0: Renders during the depth pass. This splits the depth pass into 2 phases: with and without velocity.\n") \
|
|
TEXT(" 1: Renders during the regular base pass. This adds an extra GBuffer target during base pass rendering.") \
|
|
TEXT(" 2: Renders after the regular base pass.\n"), \
|
|
ECVF_ReadOnly | ECVF_RenderThreadSafe);
|
|
|
|
static TAutoConsoleVariable<int32> CVarBasePassOutputsVelocity(
|
|
TEXT("r.BasePassOutputsVelocity"),
|
|
-1,
|
|
TEXT("Deprecated CVar. Use r.VelocityOutputPass instead.\n"),
|
|
ECVF_ReadOnly | ECVF_RenderThreadSafe);
|
|
|
|
static TAutoConsoleVariable<int32> CVarVertexDeformationOutputsVelocity(
|
|
TEXT("r.VertexDeformationOutputsVelocity"),
|
|
-1,
|
|
TEXT("Deprecated CVar. Use r.Velocity.EnableVertexDeformation instead.\n"),
|
|
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
|
|
);
|
|
|
|
DECLARE_GPU_DRAWCALL_STAT_NAMED(RenderVelocities, TEXT("Render Velocities"));
|
|
|
|
/** Validate that deprecated CVars are no longer set. */
|
|
inline void ValidateVelocityCVars()
|
|
{
|
|
#if !UE_BUILD_SHIPPING
|
|
static bool bHasValidatedCVars = false;
|
|
if (!bHasValidatedCVars)
|
|
{
|
|
{
|
|
const int32 Value = CVarBasePassOutputsVelocity.GetValueOnAnyThread();
|
|
if (Value != -1)
|
|
{
|
|
UE_LOG(LogRenderer, Warning, TEXT("Deprecated CVar r.BasePassOutputsVelocity is set to %d. Remove and use r.VelocityOutputPass instead."), Value);
|
|
}
|
|
}
|
|
{
|
|
const int32 Value = CVarVertexDeformationOutputsVelocity.GetValueOnAnyThread();
|
|
if (Value != -1)
|
|
{
|
|
UE_LOG(LogRenderer, Warning, TEXT("Deprecated CVar r.VertexDeformationOutputsVelocity is set to %d. Remove and use r.Velocity.EnableVertexDeformation instead."), Value);
|
|
}
|
|
}
|
|
bHasValidatedCVars = true;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
class FVelocityVS : public FMeshMaterialShader
|
|
{
|
|
public:
|
|
DECLARE_SHADER_TYPE(FVelocityVS, MeshMaterial);
|
|
|
|
static bool ShouldCompilePermutation(const FMeshMaterialShaderPermutationParameters& Parameters)
|
|
{
|
|
// Compile for default material.
|
|
const bool bIsDefault = Parameters.MaterialParameters.bIsSpecialEngineMaterial;
|
|
|
|
// Compile for masked materials.
|
|
const bool bIsMasked = !Parameters.MaterialParameters.bWritesEveryPixel;
|
|
|
|
// Compile for opaque and two-sided materials.
|
|
const bool bIsOpaqueAndTwoSided = (Parameters.MaterialParameters.bIsTwoSided && !IsTranslucentBlendMode(Parameters.MaterialParameters));
|
|
|
|
// Compile for materials which modify meshes.
|
|
const bool bMayModifyMeshes = Parameters.MaterialParameters.bMaterialMayModifyMeshPosition;
|
|
|
|
const bool bHasPlatformSupport = PlatformSupportsVelocityRendering(Parameters.Platform);
|
|
|
|
/**
|
|
* If we don't use base pass velocity then we may need to generate permutations for this shader.
|
|
* We only need to compile shaders which aren't considered "simple" enough to swap against the default material.
|
|
* This massively simplifies the calculations.
|
|
*/
|
|
const bool bIsSeparateVelocityPassRequired =
|
|
!FVelocityRendering::BasePassCanOutputVelocity(Parameters.Platform) &&
|
|
(bIsMasked || bIsOpaqueAndTwoSided || bMayModifyMeshes);
|
|
|
|
// The material may explicitly request that it be rendered into the translucent velocity pass.
|
|
const bool bIsSeparateVelocityPassRequiredByMaterial = Parameters.MaterialParameters.bIsTranslucencyWritingVelocity;
|
|
|
|
const bool bIsNaniteFactory = Parameters.VertexFactoryType->SupportsNaniteRendering();
|
|
return bHasPlatformSupport && !bIsNaniteFactory && (bIsDefault || bIsSeparateVelocityPassRequired || bIsSeparateVelocityPassRequiredByMaterial);
|
|
}
|
|
|
|
FVelocityVS() = default;
|
|
FVelocityVS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
|
|
: FMeshMaterialShader(Initializer)
|
|
{}
|
|
};
|
|
|
|
class FVelocityPS : public FMeshMaterialShader
|
|
{
|
|
public:
|
|
DECLARE_SHADER_TYPE(FVelocityPS, MeshMaterial);
|
|
|
|
static bool ShouldCompilePermutation(const FMeshMaterialShaderPermutationParameters& Parameters)
|
|
{
|
|
return FVelocityVS::ShouldCompilePermutation(Parameters);
|
|
}
|
|
|
|
static void ModifyCompilationEnvironment(const FMaterialShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
|
|
{
|
|
FMaterialShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
|
|
OutEnvironment.SetRenderTargetOutputFormat(0, PF_A16B16G16R16);
|
|
|
|
// We support velocity on thin trnaslucent only with masking, and only if the material is only made of thin translucent shading model.
|
|
OutEnvironment.SetDefine(TEXT("VELOCITY_THIN_TRANSLUCENT_MODE"), Parameters.MaterialParameters.ShadingModels.HasOnlyShadingModel(MSM_ThinTranslucent));
|
|
}
|
|
|
|
FVelocityPS() = default;
|
|
FVelocityPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
|
|
: FMeshMaterialShader(Initializer)
|
|
{}
|
|
};
|
|
|
|
IMPLEMENT_SHADER_TYPE(,FVelocityVS, TEXT("/Engine/Private/VelocityShader.usf"), TEXT("MainVertexShader"), SF_Vertex);
|
|
IMPLEMENT_SHADER_TYPE(,FVelocityPS, TEXT("/Engine/Private/VelocityShader.usf"), TEXT("MainPixelShader"), SF_Pixel);
|
|
IMPLEMENT_SHADERPIPELINE_TYPE_VSPS(VelocityPipeline, FVelocityVS, FVelocityPS, true);
|
|
|
|
EMeshPass::Type GetMeshPassFromVelocityPass(EVelocityPass VelocityPass)
|
|
{
|
|
switch (VelocityPass)
|
|
{
|
|
case EVelocityPass::Opaque:
|
|
return EMeshPass::Velocity;
|
|
case EVelocityPass::Translucent:
|
|
return EMeshPass::TranslucentVelocity;
|
|
}
|
|
check(false);
|
|
return EMeshPass::Velocity;
|
|
}
|
|
|
|
bool FDeferredShadingSceneRenderer::ShouldRenderVelocities() const
|
|
{
|
|
if (!FVelocityRendering::IsVelocityPassSupported(ShaderPlatform) || ViewFamily.UseDebugViewPS())
|
|
{
|
|
return false;
|
|
}
|
|
if (FVelocityRendering::DepthPassCanOutputVelocity(Scene->GetFeatureLevel()))
|
|
{
|
|
// Always render velocity when it is part of the depth pass to avoid dropping things from the depth pass.
|
|
// This means that we will pay the cost of velocity in the pass even if we don't really need it according to the view logic below.
|
|
// But requiring velocity is by far the most common case.
|
|
// And the alternative approach is for the depth pass to also incorporate the logic below to avoid dropping velocity primitives.
|
|
return true;
|
|
}
|
|
|
|
bool bNeedsVelocity = false;
|
|
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
|
|
{
|
|
const FViewInfo& View = Views[ViewIndex];
|
|
const FPerViewPipelineState& ViewPipelineState = GetViewPipelineState(View);
|
|
|
|
bool bTemporalAA = IsTemporalAccumulationBasedMethod(View.AntiAliasingMethod) && !View.bCameraCut;
|
|
bool bMotionBlur = IsMotionBlurEnabled(View);
|
|
bool bVisualizeMotionblur = View.Family->EngineShowFlags.VisualizeMotionBlur || View.Family->EngineShowFlags.VisualizeTemporalUpscaler;
|
|
bool bDistanceFieldAO = ShouldPrepareForDistanceFieldAO(Scene, ViewFamily, AnyViewHasGIMethodSupportingDFAO());
|
|
|
|
bool bSceneSSREnabled = ViewPipelineState.ReflectionsMethod == EReflectionsMethod::SSR && ScreenSpaceRayTracing::ShouldRenderScreenSpaceReflections(View);
|
|
bool bWaterSSREnabled = ViewPipelineState.ReflectionsMethodWater == EReflectionsMethod::SSR && ScreenSpaceRayTracing::ShouldRenderScreenSpaceReflectionsWater(View);
|
|
bool bSSRTemporal = (bSceneSSREnabled || bWaterSSREnabled) && ScreenSpaceRayTracing::IsSSRTemporalPassRequired(View);
|
|
|
|
bool bRayTracing = IsRayTracingEnabled() && View.IsRayTracingAllowedForView();
|
|
bool bDenoise = bRayTracing;
|
|
|
|
bool bSSGI = ViewPipelineState.DiffuseIndirectMethod == EDiffuseIndirectMethod::SSGI;
|
|
bool bLumen = ViewPipelineState.DiffuseIndirectMethod == EDiffuseIndirectMethod::Lumen || ViewPipelineState.ReflectionsMethod == EReflectionsMethod::Lumen;
|
|
|
|
bool bDistortion = ShouldRenderDistortion();
|
|
|
|
bNeedsVelocity |= bVisualizeMotionblur || bMotionBlur || bTemporalAA || bDistanceFieldAO || bSSRTemporal || bDenoise || bSSGI || bLumen || bDistortion;
|
|
}
|
|
|
|
return bNeedsVelocity;
|
|
}
|
|
|
|
bool FMobileSceneRenderer::ShouldRenderVelocities() const
|
|
{
|
|
if (!FVelocityRendering::IsVelocityPassSupported(ShaderPlatform) || ViewFamily.UseDebugViewPS() || !PlatformSupportsVelocityRendering(ShaderPlatform))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool bNeedsVelocity = false;
|
|
for (int32 ViewIndex = 0; ViewIndex < Views.Num() && !bNeedsVelocity; ViewIndex++)
|
|
{
|
|
const FViewInfo& View = Views[ViewIndex];
|
|
|
|
const bool bTemporalAA = IsTemporalAccumulationBasedMethod(View.AntiAliasingMethod);
|
|
const bool bIsUsingTemporalUpscaler = View.Family->GetTemporalUpscalerInterface() != nullptr;
|
|
const bool bVelocityRendering = (bIsUsingTemporalUpscaler || bTemporalAA) && !View.bCameraCut;
|
|
|
|
bNeedsVelocity |= bVelocityRendering;
|
|
}
|
|
|
|
return bNeedsVelocity;
|
|
}
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FVelocityPassParameters, )
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FViewShaderParameters, View)
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FSceneTextureShaderParameters, SceneTextures)
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FInstanceCullingDrawParams, InstanceCullingDrawParams)
|
|
RENDER_TARGET_BINDING_SLOTS()
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
void FSceneRenderer::RenderVelocities(
|
|
FRDGBuilder& GraphBuilder,
|
|
TArrayView<FViewInfo> InViews,
|
|
const FSceneTextures& SceneTextures,
|
|
EVelocityPass VelocityPass,
|
|
bool bForceVelocity,
|
|
bool bBindRenderTarget)
|
|
{
|
|
RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, RenderVelocities);
|
|
SCOPED_NAMED_EVENT(FSceneRenderer_RenderVelocities, FColor::Emerald);
|
|
SCOPE_CYCLE_COUNTER(STAT_RenderVelocities);
|
|
|
|
// Create mask for which GPUs we need clearing on
|
|
uint32 bNeedsClearMask = HasBeenProduced(SceneTextures.Velocity) ? 0 : ((1u << GNumExplicitGPUsForRendering) - 1);
|
|
|
|
RDG_EVENT_SCOPE_STAT(GraphBuilder, RenderVelocities, "RenderVelocities");
|
|
RDG_GPU_STAT_SCOPE(GraphBuilder, RenderVelocities);
|
|
|
|
const EMeshPass::Type MeshPass = GetMeshPassFromVelocityPass(VelocityPass);
|
|
FExclusiveDepthStencil ExclusiveDepthStencil = (VelocityPass == EVelocityPass::Opaque && !(Scene->EarlyZPassMode == DDM_AllOpaqueNoVelocity))
|
|
? FExclusiveDepthStencil::DepthRead_StencilWrite
|
|
: FExclusiveDepthStencil::DepthWrite_StencilWrite;
|
|
|
|
for (int32 ViewIndex = 0; ViewIndex < InViews.Num(); ViewIndex++)
|
|
{
|
|
FViewInfo& View = InViews[ViewIndex];
|
|
|
|
if (View.ShouldRenderView())
|
|
{
|
|
const bool bHasAnyDraw = HasAnyDraw(View.ParallelMeshDrawCommandPasses[MeshPass]);
|
|
if (!bHasAnyDraw && !bForceVelocity)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
|
|
|
|
const bool bIsParallelVelocity = FVelocityRendering::IsParallelVelocity(ShaderPlatform);
|
|
|
|
// Clear velocity render target explicitly when velocity rendering in parallel or no draw but force to.
|
|
// Avoid adding a separate clear pass in non parallel rendering.
|
|
const bool bExplicitlyClearVelocity = (bNeedsClearMask & View.GPUMask.GetNative()) && (bIsParallelVelocity || (bForceVelocity && !bHasAnyDraw));
|
|
|
|
if (bExplicitlyClearVelocity)
|
|
{
|
|
AddClearRenderTargetPass(GraphBuilder, SceneTextures.Velocity);
|
|
bNeedsClearMask &= ~View.GPUMask.GetNative();
|
|
}
|
|
|
|
if (!bHasAnyDraw)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
View.BeginRenderView();
|
|
|
|
FParallelMeshDrawCommandPass& ParallelMeshPass = *View.ParallelMeshDrawCommandPasses[MeshPass];
|
|
|
|
FVelocityPassParameters* PassParameters = GraphBuilder.AllocParameters<FVelocityPassParameters>();
|
|
PassParameters->View = View.GetShaderParameters();
|
|
ParallelMeshPass.BuildRenderingCommands(GraphBuilder, Scene->GPUScene, PassParameters->InstanceCullingDrawParams);
|
|
PassParameters->SceneTextures = SceneTextures.GetSceneTextureShaderParameters(View.FeatureLevel);
|
|
PassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(
|
|
SceneTextures.Depth.Resolve,
|
|
ERenderTargetLoadAction::ELoad,
|
|
ERenderTargetLoadAction::ELoad,
|
|
ExclusiveDepthStencil);
|
|
|
|
if (bBindRenderTarget)
|
|
{
|
|
PassParameters->RenderTargets[0] = FRenderTargetBinding(SceneTextures.Velocity, (bNeedsClearMask & View.GPUMask.GetNative()) ? ERenderTargetLoadAction::EClear : ERenderTargetLoadAction::ELoad);
|
|
bNeedsClearMask &= ~View.GPUMask.GetNative();
|
|
}
|
|
PassParameters->RenderTargets.MultiViewCount = (View.bIsMobileMultiViewEnabled) ? 2 : (View.Aspects.IsMobileMultiViewEnabled() ? 1 : 0);
|
|
|
|
if (bIsParallelVelocity)
|
|
{
|
|
GraphBuilder.AddDispatchPass(
|
|
RDG_EVENT_NAME("VelocityParallel"),
|
|
PassParameters,
|
|
ERDGPassFlags::Raster,
|
|
[&View, &ParallelMeshPass, PassParameters](FRDGDispatchPassBuilder& DispatchPassBuilder)
|
|
{
|
|
ParallelMeshPass.Dispatch(DispatchPassBuilder, &PassParameters->InstanceCullingDrawParams);
|
|
});
|
|
}
|
|
else
|
|
{
|
|
GraphBuilder.AddPass(
|
|
RDG_EVENT_NAME("Velocity"),
|
|
PassParameters,
|
|
ERDGPassFlags::Raster,
|
|
[&View, &ParallelMeshPass, PassParameters](FRDGAsyncTask, FRHICommandList& RHICmdList)
|
|
{
|
|
SetStereoViewport(RHICmdList, View);
|
|
ParallelMeshPass.Draw(RHICmdList, &PassParameters->InstanceCullingDrawParams);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
#if !(UE_BUILD_SHIPPING)
|
|
const bool bForwardShadingEnabled = IsForwardShadingEnabled(ShaderPlatform);
|
|
if (!bForwardShadingEnabled)
|
|
{
|
|
FRenderTargetBindingSlots VelocityRenderTargets;
|
|
VelocityRenderTargets[0] = FRenderTargetBinding(SceneTextures.Velocity, bNeedsClearMask ? ERenderTargetLoadAction::EClear : ERenderTargetLoadAction::ELoad);
|
|
VelocityRenderTargets.DepthStencil = FDepthStencilBinding(
|
|
SceneTextures.Depth.Resolve,
|
|
ERenderTargetLoadAction::ELoad,
|
|
ERenderTargetLoadAction::ELoad,
|
|
ExclusiveDepthStencil);
|
|
|
|
StampDeferredDebugProbeVelocityPS(GraphBuilder, InViews, VelocityRenderTargets);
|
|
}
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
void FMobileSceneRenderer::RenderVelocityPass(FRHICommandList& RHICmdList, const FViewInfo& View, const FInstanceCullingDrawParams* InstanceCullingDrawParams)
|
|
{
|
|
checkSlow(RHICmdList.IsInsideRenderPass());
|
|
|
|
if (auto* Pass = View.ParallelMeshDrawCommandPasses[EMeshPass::Velocity])
|
|
{
|
|
SCOPED_NAMED_EVENT(FMobileSceneRenderer_RenderVelocityPass, FColor::Emerald);
|
|
RHI_BREADCRUMB_EVENT_STAT(RHICmdList, RenderVelocities, "MobileRenderVelocityPass");
|
|
SCOPED_GPU_STAT(RHICmdList, RenderVelocities);
|
|
|
|
SCOPE_CYCLE_COUNTER(STAT_RenderVelocities);
|
|
CSV_SCOPED_TIMING_STAT_EXCLUSIVE(RenderVelocityPass);
|
|
|
|
SetStereoViewport(RHICmdList, View);
|
|
Pass->Draw(RHICmdList, InstanceCullingDrawParams);
|
|
}
|
|
}
|
|
|
|
EPixelFormat FVelocityRendering::GetFormat(EShaderPlatform ShaderPlatform)
|
|
{
|
|
// Lumen needs velocity depth
|
|
const bool bNeedVelocityDepth = (DoesProjectSupportDistanceFields() && FDataDrivenShaderPlatformInfo::GetSupportsLumenGI(ShaderPlatform))
|
|
|| FDataDrivenShaderPlatformInfo::GetSupportsRayTracing(ShaderPlatform);
|
|
|
|
// Android GLES platform doesn't support R16G16_UNORM and R16G16B16A16_UNORM format, use R16G16_UINT or R16G16B16A16_UINT instead.
|
|
const bool bIsOpenGLPlatform = IsOpenGLPlatform(ShaderPlatform);
|
|
if (bIsOpenGLPlatform)
|
|
{
|
|
return bNeedVelocityDepth ? PF_R16G16B16A16_UINT : PF_R16G16_UINT;
|
|
}
|
|
else
|
|
{
|
|
return bNeedVelocityDepth ? PF_A16B16G16R16 : PF_G16R16;
|
|
}
|
|
}
|
|
|
|
ETextureCreateFlags FVelocityRendering::GetCreateFlags(EShaderPlatform ShaderPlatform)
|
|
{
|
|
const ETextureCreateFlags FastVRamFlag = BasePassCanOutputVelocity(ShaderPlatform) ? GFastVRamConfig.GBufferVelocity : TexCreate_None;
|
|
return TexCreate_RenderTargetable | TexCreate_UAV | TexCreate_ShaderResource | FastVRamFlag;
|
|
}
|
|
|
|
FRDGTextureDesc FVelocityRendering::GetRenderTargetDesc(EShaderPlatform ShaderPlatform, FIntPoint Extent, const bool bRequireMultiView)
|
|
{
|
|
return FRDGTextureDesc::CreateRenderTargetTextureDesc(Extent, GetFormat(ShaderPlatform), FClearValueBinding::Transparent, GetCreateFlags(ShaderPlatform), bRequireMultiView);
|
|
}
|
|
|
|
bool FVelocityRendering::IsVelocityPassSupported(EShaderPlatform ShaderPlatform)
|
|
{
|
|
ValidateVelocityCVars();
|
|
|
|
return GPixelFormats[GetFormat(ShaderPlatform)].Supported;
|
|
}
|
|
|
|
bool FVelocityRendering::DepthPassCanOutputVelocity(ERHIFeatureLevel::Type FeatureLevel)
|
|
{
|
|
static bool bRequestedDepthPassVelocity = CVarVelocityOutputPass.GetValueOnAnyThread() == 0;
|
|
const bool bMSAAEnabled = GetDefaultMSAACount(FeatureLevel) > 1;
|
|
return !bMSAAEnabled && bRequestedDepthPassVelocity;
|
|
}
|
|
|
|
bool FVelocityRendering::BasePassCanOutputVelocity(EShaderPlatform ShaderPlatform)
|
|
{
|
|
return IsUsingBasePassVelocity(ShaderPlatform);
|
|
}
|
|
|
|
bool FVelocityRendering::IsParallelVelocity(EShaderPlatform ShaderPlatform)
|
|
{
|
|
return GRHICommandList.UseParallelAlgorithms() && CVarParallelVelocity.GetValueOnRenderThread()
|
|
// Parallel dispatch is not supported on mobile platform
|
|
&& !IsMobilePlatform(ShaderPlatform);
|
|
}
|
|
|
|
bool FVelocityMeshProcessor::PrimitiveHasVelocityForView(const FViewInfo& View, const FPrimitiveSceneProxy* PrimitiveSceneProxy)
|
|
{
|
|
// Skip camera cuts which effectively reset velocity for the new frame.
|
|
if (View.bCameraCut && !View.PreviousViewTransform.IsSet())
|
|
{
|
|
return false;
|
|
}
|
|
// Velocity pass not rendered for debug views.
|
|
if (View.Family->UseDebugViewPS())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const FBoxSphereBounds& PrimitiveBounds = PrimitiveSceneProxy->GetBounds();
|
|
const float LODFactorDistanceSquared = (PrimitiveBounds.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.
|
|
float MinScreenRadiusForVelocityPass = View.FinalPostProcessSettings.MotionBlurPerObjectSize * (2.0f / 100.0f);
|
|
float MinScreenRadiusForVelocityPassSquared = FMath::Square(MinScreenRadiusForVelocityPass);
|
|
|
|
// Skip primitives that only cover a small amount of screen space, motion blur on them won't be noticeable.
|
|
if (FMath::Square(PrimitiveBounds.SphereRadius) <= MinScreenRadiusForVelocityPassSquared * LODFactorDistanceSquared)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FOpaqueVelocityMeshProcessor::PrimitiveCanHaveVelocity(EShaderPlatform ShaderPlatform, const FPrimitiveSceneProxy* PrimitiveSceneProxy)
|
|
{
|
|
return PrimitiveCanHaveVelocity(ShaderPlatform, PrimitiveSceneProxy->DrawsVelocity(), PrimitiveSceneProxy->HasStaticLighting());
|
|
}
|
|
|
|
bool FOpaqueVelocityMeshProcessor::PrimitiveCanHaveVelocity(EShaderPlatform ShaderPlatform, bool bDrawVelocity, bool bHasStaticLighting)
|
|
{
|
|
if (!FVelocityRendering::IsVelocityPassSupported(ShaderPlatform) || !PlatformSupportsVelocityRendering(ShaderPlatform))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!bDrawVelocity)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FOpaqueVelocityMeshProcessor::PrimitiveHasVelocityForFrame(const FPrimitiveSceneProxy* PrimitiveSceneProxy)
|
|
{
|
|
if (!PrimitiveSceneProxy->AlwaysHasVelocity())
|
|
{
|
|
// Check if the primitive has moved.
|
|
const FPrimitiveSceneInfo* PrimitiveSceneInfo = PrimitiveSceneProxy->GetPrimitiveSceneInfo();
|
|
const FScene* Scene = PrimitiveSceneInfo->Scene;
|
|
const FMatrix& LocalToWorld = PrimitiveSceneProxy->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;
|
|
}
|
|
|
|
static bool UseDefaultMaterial(const FMaterial* Material, bool bVFTypeSupportsNullPixelShader, bool bMaterialModifiesMeshPosition)
|
|
{
|
|
// Materials without masking or custom vertex modifications can be swapped out
|
|
// for the default material, which simplifies the shader. However, the default
|
|
// material also does not support being two-sided.
|
|
return Material->WritesEveryPixel(false, bVFTypeSupportsNullPixelShader) && !Material->IsTwoSided() && !bMaterialModifiesMeshPosition;
|
|
}
|
|
|
|
bool FOpaqueVelocityMeshProcessor::TryAddMeshBatch(
|
|
const FMeshBatch& RESTRICT MeshBatch,
|
|
uint64 BatchElementMask,
|
|
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
|
|
int32 StaticMeshId,
|
|
const FMaterialRenderProxy* MaterialRenderProxy,
|
|
const FMaterial* Material)
|
|
{
|
|
const bool bIsNotTranslucent = IsOpaqueOrMaskedBlendMode(*Material);
|
|
|
|
bool bResult = true;
|
|
if (MeshBatch.bUseForMaterial && bIsNotTranslucent && ShouldIncludeMaterialInDefaultOpaquePass(*Material))
|
|
{
|
|
// This is specifically done *before* the material swap, as swapped materials may have different fill / cull modes.
|
|
const FMeshDrawingPolicyOverrideSettings OverrideSettings = ComputeMeshOverrideSettings(MeshBatch);
|
|
const ERasterizerFillMode MeshFillMode = ComputeMeshFillMode(*Material, OverrideSettings);
|
|
const ERasterizerCullMode MeshCullMode = ComputeMeshCullMode(*Material, OverrideSettings);
|
|
const bool bVFTypeSupportsNullPixelShader = MeshBatch.VertexFactory->SupportsNullPixelShader();
|
|
const bool bModifiesMeshPosition = DoMaterialAndPrimitiveModifyMeshPosition(*Material, PrimitiveSceneProxy);
|
|
|
|
const bool bSwapWithDefaultMaterial = UseDefaultMaterial(Material, bVFTypeSupportsNullPixelShader, bModifiesMeshPosition);
|
|
if (bSwapWithDefaultMaterial)
|
|
{
|
|
MaterialRenderProxy = UMaterial::GetDefaultMaterial(MD_Surface)->GetRenderProxy();
|
|
Material = MaterialRenderProxy->GetMaterialNoFallback(FeatureLevel);
|
|
}
|
|
|
|
check(Material && MaterialRenderProxy);
|
|
|
|
bResult = Process(MeshBatch, BatchElementMask, StaticMeshId, PrimitiveSceneProxy, *MaterialRenderProxy, *Material, MeshFillMode, MeshCullMode);
|
|
}
|
|
return bResult;
|
|
}
|
|
|
|
void FOpaqueVelocityMeshProcessor::AddMeshBatch(
|
|
const FMeshBatch& RESTRICT MeshBatch,
|
|
uint64 BatchElementMask,
|
|
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
|
|
int32 StaticMeshId)
|
|
{
|
|
const EShaderPlatform ShaderPlatform = GetFeatureLevelShaderPlatform(FeatureLevel);
|
|
if (!PrimitiveCanHaveVelocity(ShaderPlatform, PrimitiveSceneProxy))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (ViewIfDynamicMeshCommand)
|
|
{
|
|
if (!PrimitiveHasVelocityForFrame(PrimitiveSceneProxy))
|
|
{
|
|
return;
|
|
}
|
|
|
|
checkSlow(ViewIfDynamicMeshCommand->bIsViewInfo);
|
|
FViewInfo* ViewInfo = (FViewInfo*)ViewIfDynamicMeshCommand;
|
|
|
|
if (!PrimitiveHasVelocityForView(*ViewInfo, PrimitiveSceneProxy))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
const FMaterialRenderProxy* MaterialRenderProxy = MeshBatch.MaterialRenderProxy;
|
|
while (MaterialRenderProxy)
|
|
{
|
|
const FMaterial* Material = MaterialRenderProxy->GetMaterialNoFallback(FeatureLevel);
|
|
if (Material && Material->GetRenderingThreadShaderMap())
|
|
{
|
|
if (TryAddMeshBatch(MeshBatch, BatchElementMask, PrimitiveSceneProxy, StaticMeshId, MaterialRenderProxy, Material))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
MaterialRenderProxy = MaterialRenderProxy->GetFallback(FeatureLevel);
|
|
}
|
|
}
|
|
|
|
void FOpaqueVelocityMeshProcessor::CollectPSOInitializers(const FSceneTexturesConfig& SceneTexturesConfig, const FMaterial& Material, const FPSOPrecacheVertexFactoryData& VertexFactoryData, const FPSOPrecacheParams& PreCacheParams, TArray<FPSOPrecacheData>& PSOInitializers)
|
|
{
|
|
const EShaderPlatform ShaderPlatform = GetFeatureLevelShaderPlatform(FeatureLevel);
|
|
bool bDrawsVelocity = (PreCacheParams.Mobility == EComponentMobility::Movable || PreCacheParams.Mobility == EComponentMobility::Stationary);
|
|
bDrawsVelocity = bDrawsVelocity || (/*VertexDeformationOutputsVelocity() &&*/ (PreCacheParams.bAnyMaterialHasWorldPositionOffset || Material.MaterialUsesWorldPositionOffset_GameThread()));
|
|
if (!PrimitiveCanHaveVelocity(ShaderPlatform, bDrawsVelocity, PreCacheParams.bStaticLighting))
|
|
{
|
|
return;
|
|
}
|
|
|
|
const FMaterial* EffectiveMaterial = &Material;
|
|
|
|
bool bCollectPSOs = false;
|
|
if (PreCacheParams.bDefaultMaterial)
|
|
{
|
|
// Precache all cull modes for default material?
|
|
bCollectPSOs = true;
|
|
}
|
|
else
|
|
{
|
|
const bool bIsNotTranslucent = IsOpaqueOrMaskedBlendMode(Material);
|
|
|
|
if (PreCacheParams.bRenderInMainPass && bIsNotTranslucent && ShouldIncludeMaterialInDefaultOpaquePass(Material))
|
|
{
|
|
const bool bVFTypeSupportsNullPixelShader = VertexFactoryData.VertexFactoryType->SupportsNullPixelShader();
|
|
const bool bUseDefaultMaterial = UseDefaultMaterial(&Material, bVFTypeSupportsNullPixelShader, Material.MaterialModifiesMeshPosition_GameThread());
|
|
if (!bUseDefaultMaterial)
|
|
{
|
|
bCollectPSOs = true;
|
|
}
|
|
else if (VertexFactoryData.CustomDefaultVertexDeclaration)
|
|
{
|
|
EMaterialQualityLevel::Type ActiveQualityLevel = GetCachedScalabilityCVars().MaterialQualityLevel;
|
|
EffectiveMaterial = UMaterial::GetDefaultMaterial(MD_Surface)->GetMaterialResource(FeatureLevel, ActiveQualityLevel);
|
|
bCollectPSOs = true;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if (bCollectPSOs)
|
|
{
|
|
const FMeshDrawingPolicyOverrideSettings OverrideSettings = ComputeMeshOverrideSettings(PreCacheParams);
|
|
const ERasterizerFillMode MeshFillMode = ComputeMeshFillMode(Material, OverrideSettings);
|
|
const ERasterizerCullMode MeshCullMode = ComputeMeshCullMode(Material, OverrideSettings);
|
|
if (!CollectPSOInitializersInternal(SceneTexturesConfig, VertexFactoryData, *EffectiveMaterial, MeshFillMode, MeshCullMode, PSOInitializers))
|
|
{
|
|
// try again with default material (should use fallback material proxy here but currently only have FMaterial during PSO precaching)
|
|
EMaterialQualityLevel::Type ActiveQualityLevel = GetCachedScalabilityCVars().MaterialQualityLevel;
|
|
const FMaterial* DefaultMaterial = UMaterial::GetDefaultMaterial(MD_Surface)->GetMaterialResource(FeatureLevel, ActiveQualityLevel);
|
|
if (DefaultMaterial != EffectiveMaterial)
|
|
{
|
|
CollectPSOInitializersInternal(SceneTexturesConfig, VertexFactoryData, *DefaultMaterial, MeshFillMode, MeshCullMode, PSOInitializers);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool FTranslucentVelocityMeshProcessor::PrimitiveCanHaveVelocity(EShaderPlatform ShaderPlatform, const FPrimitiveSceneProxy* PrimitiveSceneProxy)
|
|
{
|
|
/**
|
|
* Velocity for translucency is always relevant because the pass also writes depth.
|
|
* Therefore, the primitive can't be filtered based on motion, or it will break post
|
|
* effects like depth of field which rely on depth information.
|
|
*/
|
|
return FVelocityRendering::IsVelocityPassSupported(ShaderPlatform) && PlatformSupportsVelocityRendering(ShaderPlatform);
|
|
}
|
|
|
|
bool FTranslucentVelocityMeshProcessor::PrimitiveHasVelocityForFrame(const FPrimitiveSceneProxy* PrimitiveSceneProxy)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool FTranslucentVelocityMeshProcessor::TryAddMeshBatch(
|
|
const FMeshBatch& RESTRICT MeshBatch,
|
|
uint64 BatchElementMask,
|
|
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
|
|
int32 StaticMeshId,
|
|
const FMaterialRenderProxy* MaterialRenderProxy,
|
|
const FMaterial* Material)
|
|
{
|
|
// Whether the primitive is marked to write translucent velocity / depth.
|
|
const bool bMaterialWritesVelocity = Material->IsTranslucencyWritingVelocity();
|
|
|
|
bool bResult = true;
|
|
if (MeshBatch.bUseForMaterial && bMaterialWritesVelocity)
|
|
{
|
|
const FMeshDrawingPolicyOverrideSettings OverrideSettings = ComputeMeshOverrideSettings(MeshBatch);
|
|
const ERasterizerFillMode MeshFillMode = ComputeMeshFillMode(*Material, OverrideSettings);
|
|
const ERasterizerCullMode MeshCullMode = ComputeMeshCullMode(*Material, OverrideSettings);
|
|
|
|
bResult = Process(MeshBatch, BatchElementMask, StaticMeshId, PrimitiveSceneProxy, *MaterialRenderProxy, *Material, MeshFillMode, MeshCullMode);
|
|
}
|
|
return bResult;
|
|
}
|
|
|
|
void FTranslucentVelocityMeshProcessor::AddMeshBatch(
|
|
const FMeshBatch& RESTRICT MeshBatch,
|
|
uint64 BatchElementMask,
|
|
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
|
|
int32 StaticMeshId)
|
|
{
|
|
const EShaderPlatform ShaderPlatform = GetFeatureLevelShaderPlatform(FeatureLevel);
|
|
|
|
if (!PrimitiveCanHaveVelocity(ShaderPlatform, PrimitiveSceneProxy))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (ViewIfDynamicMeshCommand)
|
|
{
|
|
if (!PrimitiveHasVelocityForFrame(PrimitiveSceneProxy))
|
|
{
|
|
return;
|
|
}
|
|
|
|
checkSlow(ViewIfDynamicMeshCommand->bIsViewInfo);
|
|
FViewInfo* ViewInfo = (FViewInfo*)ViewIfDynamicMeshCommand;
|
|
|
|
if (!PrimitiveHasVelocityForView(*ViewInfo, PrimitiveSceneProxy))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
const FMaterialRenderProxy* MaterialRenderProxy = MeshBatch.MaterialRenderProxy;
|
|
while (MaterialRenderProxy)
|
|
{
|
|
const FMaterial* Material = MaterialRenderProxy->GetMaterialNoFallback(FeatureLevel);
|
|
if (Material)
|
|
{
|
|
if (TryAddMeshBatch(MeshBatch, BatchElementMask, PrimitiveSceneProxy, StaticMeshId, MaterialRenderProxy, Material))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
MaterialRenderProxy = MaterialRenderProxy->GetFallback(FeatureLevel);
|
|
}
|
|
}
|
|
|
|
void FTranslucentVelocityMeshProcessor::CollectPSOInitializers(const FSceneTexturesConfig& SceneTexturesConfig, const FMaterial& Material, const FPSOPrecacheVertexFactoryData& VertexFactoryData, const FPSOPrecacheParams& PreCacheParams, TArray<FPSOPrecacheData>& PSOInitializers
|
|
)
|
|
{
|
|
const EShaderPlatform ShaderPlatform = GetFeatureLevelShaderPlatform(FeatureLevel);
|
|
if (!PrimitiveCanHaveVelocity(ShaderPlatform, nullptr))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Whether the primitive is marked to write translucent velocity / depth.
|
|
const bool bMaterialWritesVelocity = Material.IsTranslucencyWritingVelocity();
|
|
if (PreCacheParams.bRenderInMainPass && bMaterialWritesVelocity)
|
|
{
|
|
const FMeshDrawingPolicyOverrideSettings OverrideSettings = ComputeMeshOverrideSettings(PreCacheParams);
|
|
const ERasterizerFillMode MeshFillMode = ComputeMeshFillMode(Material, OverrideSettings);
|
|
const ERasterizerCullMode MeshCullMode = ComputeMeshCullMode(Material, OverrideSettings);
|
|
CollectPSOInitializersInternal(SceneTexturesConfig, VertexFactoryData, Material, MeshFillMode, MeshCullMode, PSOInitializers);
|
|
}
|
|
}
|
|
|
|
bool GetVelocityPassShaders(
|
|
const FMaterial& Material,
|
|
const FVertexFactoryType* VertexFactoryType,
|
|
ERHIFeatureLevel::Type FeatureLevel,
|
|
TShaderRef<FVelocityVS>& VertexShader,
|
|
TShaderRef<FVelocityPS>& PixelShader)
|
|
{
|
|
FMaterialShaderTypes ShaderTypes;
|
|
|
|
// Don't use pipeline if we have hull/domain shaders
|
|
ShaderTypes.PipelineType = &VelocityPipeline;
|
|
|
|
ShaderTypes.AddShaderType<FVelocityVS>();
|
|
ShaderTypes.AddShaderType<FVelocityPS>();
|
|
|
|
FMaterialShaders Shaders;
|
|
if (!Material.TryGetShaders(ShaderTypes, VertexFactoryType, Shaders))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Shaders.TryGetVertexShader(VertexShader);
|
|
Shaders.TryGetPixelShader(PixelShader);
|
|
return true;
|
|
}
|
|
|
|
bool 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,
|
|
FVelocityPS> VelocityPassShaders;
|
|
|
|
if (!GetVelocityPassShaders(
|
|
MaterialResource,
|
|
VertexFactory->GetType(),
|
|
FeatureLevel,
|
|
VelocityPassShaders.VertexShader,
|
|
VelocityPassShaders.PixelShader))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// When velocity is used as a depth pass we need to set a correct stencil state on mobile
|
|
if (FeatureLevel == ERHIFeatureLevel::ES3_1 && EarlyZPassMode == DDM_AllOpaqueNoVelocity)
|
|
{
|
|
extern void SetMobileBasePassDepthState(FMeshPassProcessorRenderState& DrawRenderState, const FPrimitiveSceneProxy* PrimitiveSceneProxy, const FMaterial& Material, FMaterialShadingModelField ShadingModels, bool bUsesDeferredShading);
|
|
|
|
// *Don't* get shading models from MaterialResource since it's for a default material
|
|
FMaterialShadingModelField ShadingModels = MeshBatch.MaterialRenderProxy->GetIncompleteMaterialWithFallback(ERHIFeatureLevel::ES3_1).GetShadingModels();
|
|
bool bUsesDeferredShading = IsMobileDeferredShadingEnabled(GetFeatureLevelShaderPlatform(FeatureLevel));
|
|
SetMobileBasePassDepthState(PassDrawRenderState, PrimitiveSceneProxy, MaterialResource, ShadingModels, bUsesDeferredShading);
|
|
}
|
|
|
|
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);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FVelocityMeshProcessor::CollectPSOInitializersInternal(
|
|
const FSceneTexturesConfig& SceneTexturesConfig,
|
|
const FPSOPrecacheVertexFactoryData& VertexFactoryData,
|
|
const FMaterial& RESTRICT MaterialResource,
|
|
ERasterizerFillMode MeshFillMode,
|
|
ERasterizerCullMode MeshCullMode,
|
|
TArray<FPSOPrecacheData>& PSOInitializers)
|
|
{
|
|
TMeshProcessorShaders<
|
|
FVelocityVS,
|
|
FVelocityPS> VelocityPassShaders;
|
|
|
|
if (!GetVelocityPassShaders(
|
|
MaterialResource,
|
|
VertexFactoryData.VertexFactoryType,
|
|
FeatureLevel,
|
|
VelocityPassShaders.VertexShader,
|
|
VelocityPassShaders.PixelShader))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
EShaderPlatform ShaderPlatform = GetFeatureLevelShaderPlatform(FeatureLevel);
|
|
|
|
FGraphicsPipelineRenderTargetsInfo RenderTargetsInfo;
|
|
RenderTargetsInfo.NumSamples = 1;
|
|
AddRenderTargetInfo(FVelocityRendering::GetFormat(ShaderPlatform), FVelocityRendering::GetCreateFlags(ShaderPlatform), RenderTargetsInfo);
|
|
{
|
|
ETextureCreateFlags DepthStencilCreateFlags = SceneTexturesConfig.DepthCreateFlags;
|
|
SetupDepthStencilInfo(PF_DepthStencil, DepthStencilCreateFlags, ERenderTargetLoadAction::ELoad,
|
|
ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthWrite_StencilWrite, RenderTargetsInfo);
|
|
}
|
|
|
|
AddGraphicsPipelineStateInitializer(
|
|
VertexFactoryData,
|
|
MaterialResource,
|
|
PassDrawRenderState,
|
|
RenderTargetsInfo,
|
|
VelocityPassShaders,
|
|
MeshFillMode,
|
|
MeshCullMode,
|
|
PT_TriangleList,
|
|
EMeshPassFeatures::Default,
|
|
true /*bRequired*/,
|
|
PSOInitializers);
|
|
|
|
return true;
|
|
}
|
|
|
|
FVelocityMeshProcessor::FVelocityMeshProcessor(EMeshPass::Type MeshPassType, const FScene* Scene, ERHIFeatureLevel::Type FeatureLevel, const FSceneView* InViewIfDynamicMeshCommand, const FMeshPassProcessorRenderState& InPassDrawRenderState, FMeshPassDrawListContext* InDrawListContext)
|
|
: FMeshPassProcessor(MeshPassType, Scene, FeatureLevel, InViewIfDynamicMeshCommand, InDrawListContext)
|
|
, PassDrawRenderState(InPassDrawRenderState)
|
|
{}
|
|
|
|
FOpaqueVelocityMeshProcessor::FOpaqueVelocityMeshProcessor(const FScene* Scene, ERHIFeatureLevel::Type FeatureLevel, const FSceneView* InViewIfDynamicMeshCommand, const FMeshPassProcessorRenderState& InPassDrawRenderState, FMeshPassDrawListContext* InDrawListContext, EDepthDrawingMode InEarlyZPassMode)
|
|
: FVelocityMeshProcessor(EMeshPass::Velocity, Scene, FeatureLevel, InViewIfDynamicMeshCommand, InPassDrawRenderState, InDrawListContext)
|
|
{
|
|
EarlyZPassMode = InEarlyZPassMode;
|
|
}
|
|
|
|
FMeshPassProcessor* CreateVelocityPassProcessor(ERHIFeatureLevel::Type FeatureLevel, const FScene* Scene, const FSceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext)
|
|
{
|
|
EDepthDrawingMode EarlyZPassMode;
|
|
bool bEarlyZPassMovable;
|
|
FScene::GetEarlyZPassMode(FeatureLevel, EarlyZPassMode, bEarlyZPassMovable);
|
|
|
|
FMeshPassProcessorRenderState VelocityPassState;
|
|
VelocityPassState.SetBlendState(TStaticBlendState<CW_RGBA>::GetRHI());
|
|
VelocityPassState.SetDepthStencilState((EarlyZPassMode == DDM_AllOpaqueNoVelocity) // if the depth mode is all opaque except velocity, it relies on velocity to write the depth of the remaining meshes
|
|
? TStaticDepthStencilState<true, CF_DepthNearOrEqual>::GetRHI()
|
|
: TStaticDepthStencilState<false, CF_DepthNearOrEqual>::GetRHI());
|
|
|
|
return new FOpaqueVelocityMeshProcessor(Scene, FeatureLevel, InViewIfDynamicMeshCommand, VelocityPassState, InDrawListContext, EarlyZPassMode);
|
|
}
|
|
|
|
REGISTER_MESHPASSPROCESSOR_AND_PSOCOLLECTOR(VelocityPass, CreateVelocityPassProcessor, EShadingPath::Deferred, EMeshPass::Velocity, EMeshPassFlags::CachedMeshCommands | EMeshPassFlags::MainView);
|
|
REGISTER_MESHPASSPROCESSOR_AND_PSOCOLLECTOR(MobileVelocityPass, CreateVelocityPassProcessor, EShadingPath::Mobile, EMeshPass::Velocity, EMeshPassFlags::CachedMeshCommands | EMeshPassFlags::MainView);
|
|
|
|
FTranslucentVelocityMeshProcessor::FTranslucentVelocityMeshProcessor(const FScene* Scene, ERHIFeatureLevel::Type FeatureLevel, const FSceneView* InViewIfDynamicMeshCommand, const FMeshPassProcessorRenderState& InPassDrawRenderState, FMeshPassDrawListContext* InDrawListContext)
|
|
: FVelocityMeshProcessor(EMeshPass::TranslucentVelocity, Scene, FeatureLevel, InViewIfDynamicMeshCommand, InPassDrawRenderState, InDrawListContext)
|
|
{}
|
|
|
|
FMeshPassProcessor* CreateTranslucentVelocityPassProcessor(ERHIFeatureLevel::Type FeatureLevel, const FScene* Scene, const FSceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext)
|
|
{
|
|
FMeshPassProcessorRenderState VelocityPassState;
|
|
VelocityPassState.SetBlendState(TStaticBlendState<CW_RGBA>::GetRHI());
|
|
VelocityPassState.SetDepthStencilState(TStaticDepthStencilState<true, CF_DepthNearOrEqual>::GetRHI());
|
|
|
|
return new FTranslucentVelocityMeshProcessor(Scene, FeatureLevel, InViewIfDynamicMeshCommand, VelocityPassState, InDrawListContext);
|
|
}
|
|
|
|
REGISTER_MESHPASSPROCESSOR_AND_PSOCOLLECTOR(TranslucentVelocityPass, CreateTranslucentVelocityPassProcessor, EShadingPath::Deferred, EMeshPass::TranslucentVelocity, EMeshPassFlags::CachedMeshCommands | EMeshPassFlags::MainView);
|
|
REGISTER_MESHPASSPROCESSOR_AND_PSOCOLLECTOR(MobileTranslucentVelocityPass, CreateTranslucentVelocityPassProcessor, EShadingPath::Mobile, EMeshPass::TranslucentVelocity, EMeshPassFlags::CachedMeshCommands | EMeshPassFlags::MainView);
|