// 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 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 CVarBasePassOutputsVelocity( TEXT("r.BasePassOutputsVelocity"), -1, TEXT("Deprecated CVar. Use r.VelocityOutputPass instead.\n"), ECVF_ReadOnly | ECVF_RenderThreadSafe); static TAutoConsoleVariable CVarVertexDeformationOutputsVelocity( TEXT("r.VertexDeformationOutputsVelocity"), -1, TEXT("Deprecated CVar. Use r.Velocity.EnableVertexDeformation instead.\n"), ECVF_ReadOnly | ECVF_RenderThreadSafe); static TAutoConsoleVariable 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 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(); 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& 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& 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& VertexShader, TShaderRef& PixelShader) { FMaterialShaderTypes ShaderTypes; // Don't use pipeline if we have hull/domain shaders ShaderTypes.PipelineType = &VelocityPipeline; ShaderTypes.AddShaderType(); ShaderTypes.AddShaderType(); 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& 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::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::GetRHI() : TStaticDepthStencilState::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::GetRHI()); VelocityPassState.SetDepthStencilState(TStaticDepthStencilState::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);