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