Files
UnrealEngineUWP/Engine/Source/Runtime/Renderer/Private/Renderer.cpp
Andriy Tylychko d6e66144b2 fixed undefined order of static destruction for FSceneRenderer::OcclusionSubmittedFence
#preflight 627e620d7c26e247734d2d70

[CL 20180722 by Andriy Tylychko in ue5-main branch]
2022-05-13 10:02:16 -04:00

542 lines
19 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
Renderer.cpp: Renderer module implementation.
=============================================================================*/
#include "CoreMinimal.h"
#include "Misc/CoreMisc.h"
#include "Stats/Stats.h"
#include "Modules/ModuleManager.h"
#include "Async/TaskGraphInterfaces.h"
#include "EngineDefines.h"
#include "EngineGlobals.h"
#include "RenderingThread.h"
#include "RHIStaticStates.h"
#include "SceneView.h"
#include "RenderTargetPool.h"
#include "PostProcess/SceneRenderTargets.h"
#include "VisualizeTexture.h"
#include "SceneCore.h"
#include "SceneHitProxyRendering.h"
#include "SceneRendering.h"
#include "BasePassRendering.h"
#include "MobileBasePassRendering.h"
#include "TranslucentRendering.h"
#include "RendererModule.h"
#include "GPUBenchmark.h"
#include "SystemSettings.h"
#include "VisualizeTexturePresent.h"
#include "MeshPassProcessor.inl"
#include "DebugViewModeRendering.h"
#include "EditorPrimitivesRendering.h"
#include "VisualizeTexturePresent.h"
#include "ScreenSpaceDenoise.h"
#include "VT/VirtualTextureSystem.h"
#include "PostProcess/TemporalAA.h"
#include "CanvasRender.h"
#include "RendererOnScreenNotification.h"
#include "Lumen/Lumen.h"
DEFINE_LOG_CATEGORY(LogRenderer);
IMPLEMENT_MODULE(FRendererModule, Renderer);
#if !IS_MONOLITHIC
// visual studio cannot find cross dll data for visualizers
// thus as a workaround for now, copy and paste this into every module
// where we need to visualize SystemSettings
FSystemSettings* GSystemSettingsForVisualizers = &GSystemSettings;
#endif
static int32 bFlushRenderTargetsOnWorldCleanup = 1;
FAutoConsoleVariableRef CVarFlushRenderTargetsOnWorldCleanup(TEXT("r.bFlushRenderTargetsOnWorldCleanup"), bFlushRenderTargetsOnWorldCleanup, TEXT(""));
void FRendererModule::StartupModule()
{
GScreenSpaceDenoiser = IScreenSpaceDenoiser::GetDefaultDenoiser();
FRendererOnScreenNotification::Get();
FVirtualTextureSystem::Initialize();
StopRenderingThreadDelegate = RegisterStopRenderingThreadDelegate(FStopRenderingThreadDelegate::CreateLambda([this]
{
ENQUEUE_RENDER_COMMAND(FSceneRendererCleanUp)(
[](FRHICommandListImmediate& RHICmdList)
{
FSceneRenderer::CleanUp(RHICmdList);
});
}));
}
void FRendererModule::ShutdownModule()
{
UnregisterStopRenderingThreadDelegate(StopRenderingThreadDelegate);
FVirtualTextureSystem::Shutdown();
FRendererOnScreenNotification::TearDown();
// Free up the memory of the default denoiser. Responsibility of the plugin to free up theirs.
delete IScreenSpaceDenoiser::GetDefaultDenoiser();
// Free up global resources in Lumen
Lumen::Shutdown();
void CleanupOcclusionSubmittedFence();
CleanupOcclusionSubmittedFence();
}
void FRendererModule::OnWorldCleanup(UWorld* World, bool bSessionEnded, bool bCleanupResources, bool bWorldChanged)
{
FSceneInterface* Scene = World->Scene;
ENQUEUE_RENDER_COMMAND(OnWorldCleanup)(
[Scene, bWorldChanged](FRHICommandListImmediate& RHICmdList)
{
if(bFlushRenderTargetsOnWorldCleanup > 0)
{
GRenderTargetPool.FreeUnusedResources();
}
if(bWorldChanged && Scene)
{
Scene->OnWorldCleanup();
}
});
}
void FRendererModule::InitializeSystemTextures(FRHICommandListImmediate& RHICmdList)
{
GSystemTextures.InitializeTextures(RHICmdList, GMaxRHIFeatureLevel);
}
BEGIN_SHADER_PARAMETER_STRUCT(FDrawTileMeshPassParameters, )
SHADER_PARAMETER_STRUCT_INCLUDE(FViewShaderParameters, View)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FInstanceCullingGlobalUniforms, InstanceCulling)
SHADER_PARAMETER_STRUCT_REF(FReflectionCaptureShaderData, ReflectionCapture)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FDebugViewModePassUniformParameters, DebugViewMode)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FTranslucentBasePassUniformParameters, TranslucentBasePass)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FOpaqueBasePassUniformParameters, OpaqueBasePass)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FMobileBasePassUniformParameters, MobileBasePass)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
void FRendererModule::DrawTileMesh(FCanvasRenderContext& RenderContext, FMeshPassProcessorRenderState& DrawRenderState, const FSceneView& SceneView, FMeshBatch& Mesh, bool bIsHitTesting, const FHitProxyId& HitProxyId, bool bUse128bitRT)
{
if (!GUsingNullRHI)
{
// Create an FViewInfo so we can initialize its RHI resources
//@todo - reuse this view for multiple tiles, this is going to be slow for each tile
FViewInfo& View = *RenderContext.Alloc<FViewInfo>(&SceneView);
View.ViewRect = View.UnscaledViewRect;
const auto FeatureLevel = View.GetFeatureLevel();
const EShadingPath ShadingPath = FSceneInterface::GetShadingPath(FeatureLevel);
const FSceneViewFamily* ViewFamily = View.Family;
FScene* Scene = nullptr;
if (ViewFamily->Scene)
{
Scene = ViewFamily->Scene->GetRenderScene();
}
Mesh.MaterialRenderProxy->UpdateUniformExpressionCacheIfNeeded(FeatureLevel);
FMaterialRenderProxy::UpdateDeferredCachedUniformExpressions();
FSinglePrimitiveStructured& SinglePrimitiveStructured = GTilePrimitiveBuffer;
if (Mesh.VertexFactory->GetPrimitiveIdStreamIndex(FeatureLevel, EVertexInputStreamType::PositionOnly) >= 0)
{
FMeshBatchElement& MeshElement = Mesh.Elements[0];
checkf(Mesh.Elements.Num() == 1, TEXT("Only 1 batch element currently supported by DrawTileMesh"));
checkf(MeshElement.PrimitiveUniformBuffer == nullptr, TEXT("DrawTileMesh does not currently support an explicit primitive uniform buffer on vertex factories which manually fetch primitive data. Use PrimitiveUniformBufferResource instead."));
if (MeshElement.PrimitiveUniformBufferResource)
{
checkf(MeshElement.NumInstances == 1, TEXT("DrawTileMesh does not currently support instancing"));
// Force PrimitiveId to be 0 in the shader
MeshElement.PrimitiveIdMode = PrimID_ForceZero;
// Set the LightmapID to 0, since that's where our light map data resides for this primitive
FPrimitiveUniformShaderParameters PrimitiveParams = *(const FPrimitiveUniformShaderParameters*)MeshElement.PrimitiveUniformBufferResource->GetContents();
PrimitiveParams.LightmapDataIndex = 0;
PrimitiveParams.LightmapUVIndex = 0;
// Set up reference to the single-instance
PrimitiveParams.InstanceSceneDataOffset = 0;
PrimitiveParams.NumInstanceSceneDataEntries = 1;
PrimitiveParams.InstancePayloadDataOffset = INDEX_NONE;
PrimitiveParams.InstancePayloadDataStride = 0;
// Now we just need to fill out the first entry of primitive data in a buffer and bind it
SinglePrimitiveStructured.PrimitiveSceneData = FPrimitiveSceneShaderData(PrimitiveParams);
SinglePrimitiveStructured.ShaderPlatform = View.GetShaderPlatform();
// Also fill out correct single-primitive instance data, derived from the primitive.
SinglePrimitiveStructured.InstanceSceneData.BuildInternal
(
0 /* Primitive Id */,
0 /* Relative Instance Id */,
0 /* Payload Data Flags */,
INVALID_LAST_UPDATE_FRAME,
0 /* Custom Data Count */,
0.0f /* Random ID */,
PrimitiveParams.LocalToRelativeWorld,
PrimitiveParams.PreviousLocalToRelativeWorld
);
// TODO: Payload dummy?
// Set up the parameters for the LightmapSceneData from the given LCI data
FPrecomputedLightingUniformParameters LightmapParams;
GetPrecomputedLightingParameters(FeatureLevel, LightmapParams, Mesh.LCI);
SinglePrimitiveStructured.LightmapSceneData = FLightmapSceneShaderData(LightmapParams);
SinglePrimitiveStructured.UploadToGPU();
View.PrimitiveSceneDataOverrideSRV = SinglePrimitiveStructured.PrimitiveSceneDataBufferSRV;
View.InstanceSceneDataOverrideSRV = SinglePrimitiveStructured.InstanceSceneDataBufferSRV;
View.InstancePayloadDataOverrideSRV = SinglePrimitiveStructured.InstancePayloadDataBufferSRV;
View.LightmapSceneDataOverrideSRV = SinglePrimitiveStructured.LightmapSceneDataBufferSRV;
}
}
FRDGBuilder& GraphBuilder = RenderContext.GraphBuilder;
if (!FRDGSystemTextures::IsValid(GraphBuilder))
{
FRDGSystemTextures::Create(GraphBuilder);
}
View.InitRHIResources();
View.ForwardLightingResources.SetUniformBuffer(CreateDummyForwardLightUniformBuffer(GraphBuilder));
TUniformBufferRef<FReflectionCaptureShaderData> EmptyReflectionCaptureUniformBuffer;
{
FReflectionCaptureShaderData EmptyData;
EmptyReflectionCaptureUniformBuffer = TUniformBufferRef<FReflectionCaptureShaderData>::CreateUniformBufferImmediate(EmptyData, UniformBuffer_SingleFrame);
}
//get the blend mode of the material
const FMaterial& MeshMaterial = Mesh.MaterialRenderProxy->GetIncompleteMaterialWithFallback(FeatureLevel);
const EBlendMode MaterialBlendMode = MeshMaterial.GetBlendMode();
const bool bUseVirtualTexturing = UseVirtualTexturing(FeatureLevel);
// Materials sampling VTs need FVirtualTextureSystem to be updated before being rendered :
if (bUseVirtualTexturing && !MeshMaterial.GetUniformVirtualTextureExpressions().IsEmpty())
{
RDG_GPU_STAT_SCOPE(GraphBuilder, VirtualTextureUpdate);
FVirtualTextureSystem::Get().AllocateResources(GraphBuilder, FeatureLevel);
FVirtualTextureSystem::Get().CallPendingCallbacks();
FVirtualTextureSystem::Get().Update(GraphBuilder, FeatureLevel, Scene);
}
RDG_EVENT_SCOPE(GraphBuilder, "DrawTileMesh");
auto* PassParameters = GraphBuilder.AllocParameters<FDrawTileMeshPassParameters>();
PassParameters->RenderTargets[0] = FRenderTargetBinding(RenderContext.GetRenderTarget(), ERenderTargetLoadAction::ELoad);
PassParameters->View = View.GetShaderParameters();
PassParameters->ReflectionCapture = EmptyReflectionCaptureUniformBuffer;
PassParameters->InstanceCulling = FInstanceCullingContext::CreateDummyInstanceCullingUniformBuffer(GraphBuilder);
// handle translucent material blend modes, not relevant in MaterialTexCoordScalesAnalysis since it outputs the scales.
if (ViewFamily->GetDebugViewShaderMode() == DVSM_OutputMaterialTextureScales)
{
#if WITH_DEBUG_VIEW_MODES
// make sure we are doing opaque drawing
DrawRenderState.SetBlendState(TStaticBlendState<>::GetRHI());
// is this path used on mobile?
if (ShadingPath == EShadingPath::Deferred)
{
PassParameters->DebugViewMode = CreateDebugViewModePassUniformBuffer(GraphBuilder, View, nullptr);
RenderContext.AddPass(RDG_EVENT_NAME("OutputMaterialTextureScales"), PassParameters,
[Scene, &View, &Mesh](FRHICommandListImmediate& RHICmdList)
{
DrawDynamicMeshPass(View, RHICmdList, [&](FMeshPassDrawListContext* InDrawListContext)
{
FDebugViewModeMeshProcessor PassMeshProcessor(
Scene,
View.GetFeatureLevel(),
&View,
false,
InDrawListContext);
const uint64 DefaultBatchElementMask = ~0ull;
PassMeshProcessor.AddMeshBatch(Mesh, DefaultBatchElementMask, nullptr);
});
});
}
#endif // WITH_DEBUG_VIEW_MODES
}
else if (IsTranslucentBlendMode(MaterialBlendMode))
{
if (ShadingPath == EShadingPath::Deferred)
{
PassParameters->TranslucentBasePass = CreateTranslucentBasePassUniformBuffer(GraphBuilder, Scene, View);
RenderContext.AddPass(RDG_EVENT_NAME("TranslucentDeferred"), PassParameters,
[Scene, &View, &Mesh, DrawRenderState, bUse128bitRT](FRHICommandListImmediate& RHICmdList)
{
DrawDynamicMeshPass(View, RHICmdList, [&](FDynamicPassMeshDrawListContext* DynamicMeshPassContext)
{
FBasePassMeshProcessor PassMeshProcessor(
Scene,
View.GetFeatureLevel(),
&View,
DrawRenderState,
DynamicMeshPassContext,
bUse128bitRT ? FBasePassMeshProcessor::EFlags::bRequires128bitRT : FBasePassMeshProcessor::EFlags::None,
ETranslucencyPass::TPT_AllTranslucency);
const uint64 DefaultBatchElementMask = ~0ull;
PassMeshProcessor.AddMeshBatch(Mesh, DefaultBatchElementMask, nullptr);
});
});
}
else // Mobile
{
PassParameters->MobileBasePass = CreateMobileBasePassUniformBuffer(GraphBuilder, View, EMobileBasePass::Translucent, EMobileSceneTextureSetupMode::None);
RenderContext.AddPass(RDG_EVENT_NAME("TranslucentMobile"), PassParameters,
[Scene, &View, DrawRenderState, &Mesh](FRHICommandListImmediate& RHICmdList)
{
DrawDynamicMeshPass(View, RHICmdList, [&](FDynamicPassMeshDrawListContext* DynamicMeshPassContext)
{
FMobileBasePassMeshProcessor PassMeshProcessor(
Scene,
View.GetFeatureLevel(),
&View,
DrawRenderState,
DynamicMeshPassContext,
FMobileBasePassMeshProcessor::EFlags::None,
ETranslucencyPass::TPT_AllTranslucency);
const uint64 DefaultBatchElementMask = ~0ull;
PassMeshProcessor.AddMeshBatch(Mesh, DefaultBatchElementMask, nullptr);
});
});
}
}
// handle opaque materials
else
{
// make sure we are doing opaque drawing
DrawRenderState.SetBlendState(TStaticBlendState<>::GetRHI());
// draw the mesh
if (bIsHitTesting)
{
ensureMsgf(HitProxyId == Mesh.BatchHitProxyId, TEXT("Only Mesh.BatchHitProxyId is used for hit testing."));
#if WITH_EDITOR
RenderContext.AddPass(RDG_EVENT_NAME("HitTesting"), PassParameters,
[Scene, &View, DrawRenderState, &Mesh](FRHICommandListImmediate& RHICmdList)
{
DrawDynamicMeshPass(View, RHICmdList, [&](FDynamicPassMeshDrawListContext* DynamicMeshPassContext)
{
FHitProxyMeshProcessor PassMeshProcessor(
Scene,
&View,
false,
DrawRenderState,
DynamicMeshPassContext);
const uint64 DefaultBatchElementMask = ~0ull;
PassMeshProcessor.AddMeshBatch(Mesh, DefaultBatchElementMask, nullptr);
});
});
#endif
}
else
{
if (ShadingPath == EShadingPath::Deferred)
{
PassParameters->OpaqueBasePass = CreateOpaqueBasePassUniformBuffer(GraphBuilder, View);
RenderContext.AddPass(RDG_EVENT_NAME("OpaqueDeferred"), PassParameters,
[Scene, &View, DrawRenderState, &Mesh, bUse128bitRT](FRHICommandListImmediate& RHICmdList)
{
DrawDynamicMeshPass(View, RHICmdList,
[&](FDynamicPassMeshDrawListContext* DynamicMeshPassContext)
{
FBasePassMeshProcessor PassMeshProcessor(
Scene,
View.GetFeatureLevel(),
&View,
DrawRenderState,
DynamicMeshPassContext,
bUse128bitRT ? FBasePassMeshProcessor::EFlags::bRequires128bitRT : FBasePassMeshProcessor::EFlags::None);
const uint64 DefaultBatchElementMask = ~0ull;
PassMeshProcessor.AddMeshBatch(Mesh, DefaultBatchElementMask, nullptr);
});
});
}
else // Mobile
{
PassParameters->MobileBasePass = CreateMobileBasePassUniformBuffer(GraphBuilder, View, EMobileBasePass::Opaque, EMobileSceneTextureSetupMode::None);
RenderContext.AddPass(RDG_EVENT_NAME("OpaqueMobile"), PassParameters,
[Scene, &View, DrawRenderState, &Mesh](FRHICommandListImmediate& RHICmdList)
{
DrawDynamicMeshPass(View, RHICmdList, [&](FDynamicPassMeshDrawListContext* DynamicMeshPassContext)
{
FMobileBasePassMeshProcessor PassMeshProcessor(
Scene,
View.GetFeatureLevel(),
&View,
DrawRenderState,
DynamicMeshPassContext,
FMobileBasePassMeshProcessor::EFlags::CanReceiveCSM);
const uint64 DefaultBatchElementMask = ~0ull;
PassMeshProcessor.AddMeshBatch(Mesh, DefaultBatchElementMask, nullptr);
});
});
}
}
}
}
}
void FRendererModule::DebugLogOnCrash()
{
GVisualizeTexture.DebugLogOnCrash();
GEngine->Exec(NULL, TEXT("rhi.DumpMemory"), *GLog);
// execute on main thread
{
struct FTest
{
void Thread()
{
GEngine->Exec(NULL, TEXT("Mem FromReport"), *GLog);
}
} Test;
DECLARE_CYCLE_STAT(TEXT("FSimpleDelegateGraphTask.DumpDataAfterCrash"),
STAT_FSimpleDelegateGraphTask_DumpDataAfterCrash,
STATGROUP_TaskGraphTasks);
FSimpleDelegateGraphTask::CreateAndDispatchWhenReady(
FSimpleDelegateGraphTask::FDelegate::CreateRaw(&Test, &FTest::Thread),
GET_STATID(STAT_FSimpleDelegateGraphTask_DumpDataAfterCrash), nullptr, ENamedThreads::GameThread
);
}
}
void FRendererModule::GPUBenchmark(FSynthBenchmarkResults& InOut, float WorkScale)
{
check(IsInGameThread());
FSceneViewInitOptions ViewInitOptions;
FIntRect ViewRect(0, 0, 1, 1);
FBox LevelBox(FVector(-WORLD_MAX), FVector(+WORLD_MAX));
ViewInitOptions.SetViewRectangle(ViewRect);
// Initialize Projection Matrix and ViewMatrix since FSceneView initialization is doing some math on them.
// Otherwise it trips NaN checks.
const FVector ViewPoint = LevelBox.GetCenter();
ViewInitOptions.ViewOrigin = FVector(ViewPoint.X, ViewPoint.Y, 0.0f);
ViewInitOptions.ViewRotationMatrix = FMatrix(
FPlane(1, 0, 0, 0),
FPlane(0, -1, 0, 0),
FPlane(0, 0, -1, 0),
FPlane(0, 0, 0, 1));
const float ZOffset = WORLD_MAX;
ViewInitOptions.ProjectionMatrix = FReversedZOrthoMatrix(
LevelBox.GetSize().X / 2.f,
LevelBox.GetSize().Y / 2.f,
0.5f / ZOffset,
ZOffset
);
FSceneView DummyView(ViewInitOptions);
FlushRenderingCommands();
FSynthBenchmarkResults* InOutPtr = &InOut;
ENQUEUE_RENDER_COMMAND(RendererGPUBenchmarkCommand)(
[DummyView, WorkScale, InOutPtr](FRHICommandListImmediate& RHICmdList)
{
RendererGPUBenchmark(RHICmdList, *InOutPtr, DummyView, WorkScale);
});
FlushRenderingCommands();
}
static void VisualizeTextureExec( const TCHAR* Cmd, FOutputDevice &Ar )
{
check(IsInGameThread());
FlushRenderingCommands();
GVisualizeTexture.ParseCommands(Cmd, Ar);
}
extern void NaniteStatsFilterExec(const TCHAR* Cmd, FOutputDevice& Ar);
static bool RendererExec( UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar )
{
#if SUPPORTS_VISUALIZE_TEXTURE
if (FParse::Command(&Cmd, TEXT("VisualizeTexture")) || FParse::Command(&Cmd, TEXT("Vis")))
{
VisualizeTextureExec(Cmd, Ar);
return true;
}
#endif //SUPPORTS_VISUALIZE_TEXTURE
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
if (FParse::Command(&Cmd, TEXT("DumpUnbuiltLightInteractions")))
{
InWorld->Scene->DumpUnbuiltLightInteractions(Ar);
return true;
}
else if (FParse::Command(&Cmd, TEXT("NaniteStats")))
{
NaniteStatsFilterExec(Cmd, Ar);
return true;
}
else if(FParse::Command(&Cmd, TEXT("r.RHI.Name")))
{
Ar.Logf( TEXT( "Running on the %s RHI" ), GDynamicRHI
? (GDynamicRHI->GetName() ? GDynamicRHI->GetName() : TEXT("<NULL Name>"))
: TEXT("<NULL DynamicRHI>"));
return true;
}
else if (FParse::Command(&Cmd, TEXT("r.ResetRenderTargetsExtent")))
{
ResetSceneTextureExtentHistory();
Ar.Logf(TEXT("Scene texture extent history reset. Next scene render will reallocate textures at the requested size."));
return true;
}
#endif
return false;
}
ICustomCulling* GCustomCullingImpl = nullptr;
void FRendererModule::RegisterCustomCullingImpl(ICustomCulling* impl)
{
check(GCustomCullingImpl == nullptr);
GCustomCullingImpl = impl;
}
void FRendererModule::UnregisterCustomCullingImpl(ICustomCulling* impl)
{
check(GCustomCullingImpl == impl);
GCustomCullingImpl = nullptr;
}
FStaticSelfRegisteringExec RendererExecRegistration(RendererExec);
void FRendererModule::ExecVisualizeTextureCmd( const FString& Cmd )
{
// @todo: Find a nicer way to call this
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
VisualizeTextureExec(*Cmd, *GLog);
#endif
}