723 lines
29 KiB
C++
723 lines
29 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/VirtualTextureFeedbackResource.h"
|
|
#include "VT/VirtualTextureSystem.h"
|
|
#include "PostProcess/TemporalAA.h"
|
|
#include "CanvasRender.h"
|
|
#include "RendererOnScreenNotification.h"
|
|
#include "Nanite/NaniteRayTracing.h"
|
|
#include "Lumen/Lumen.h"
|
|
#include "ScenePrivate.h"
|
|
#include "SceneUniformBuffer.h"
|
|
#include "SceneRenderTargetParameters.h"
|
|
#include "EngineModule.h"
|
|
#include "RendererInterface.h"
|
|
#include "PrimitiveSceneShaderData.h"
|
|
#include "MeshDrawCommandStats.h"
|
|
#include "LocalFogVolumeRendering.h"
|
|
#include "Rendering/RayTracingGeometryManager.h"
|
|
#include "PathTracing.h"
|
|
#include "LightFunctionAtlas.h"
|
|
#include "SceneRenderBuilder.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(""));
|
|
|
|
static int32 bBindTileMeshDrawingDummyRenderTarget = 0;
|
|
static FAutoConsoleVariableRef CVarBindTileMeshDrawingDummyRenderTarget(
|
|
TEXT("r.DrawTileMesh.DummyRT"),
|
|
bBindTileMeshDrawingDummyRenderTarget,
|
|
TEXT("Enable binding of a dummy render target for tile mesh drawing to workaround driver bugs.")
|
|
);
|
|
|
|
void FRendererModule::StartupModule()
|
|
{
|
|
#if MESH_DRAW_COMMAND_STATS
|
|
FMeshDrawCommandStatsManager::CreateInstance();
|
|
#endif
|
|
|
|
GScreenSpaceDenoiser = IScreenSpaceDenoiser::GetDefaultDenoiser();
|
|
|
|
FRendererOnScreenNotification::Get();
|
|
FVirtualTextureSystem::Initialize();
|
|
|
|
#if RHI_RAYTRACING
|
|
GRayTracingGeometryManager = new FRayTracingGeometryManager();
|
|
|
|
Nanite::GRayTracingManager.Initialize();
|
|
#endif
|
|
|
|
StopRenderingThreadDelegate = RegisterStopRenderingThreadDelegate(FStopRenderingThreadDelegate::CreateLambda([this]
|
|
{
|
|
ENQUEUE_RENDER_COMMAND(FSceneRendererCleanUp)(
|
|
[](FRHICommandListImmediate& RHICmdList)
|
|
{
|
|
FRDGBuilder::WaitForAsyncDeleteTask();
|
|
FSceneRenderBuilder::WaitForAsyncDeleteTask();
|
|
});
|
|
}));
|
|
|
|
// Needs to run on startup, after static init.
|
|
GIdentityPrimitiveUniformBuffer.InitContents();
|
|
GDistanceCullFadedInUniformBuffer.InitContents();
|
|
GDitherFadedInUniformBuffer.InitContents();
|
|
|
|
#if RHI_RAYTRACING && WITH_EDITOR
|
|
if (FApp::CanEverRender() && !FApp::IsUnattended())
|
|
{
|
|
FCoreDelegates::OnPostEngineInit.AddLambda([]() {
|
|
// We add this step via the PostEngineInit delegate so that it can run after PostInitRHI has run,
|
|
// and the rendering thread has been started so that we are able to create RTPSOs.
|
|
// For now, we only attempt to create the PathTracer RTPSO as it is the most expensive to compile by far.
|
|
// See UE-190955 for example timings.
|
|
PreparePathTracingRTPSO();
|
|
});
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void FRendererModule::ShutdownModule()
|
|
{
|
|
UnregisterStopRenderingThreadDelegate(StopRenderingThreadDelegate);
|
|
|
|
#if RHI_RAYTRACING
|
|
Nanite::GRayTracingManager.Shutdown();
|
|
|
|
delete GRayTracingGeometryManager;
|
|
GRayTracingGeometryManager = nullptr;
|
|
#endif
|
|
|
|
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_STRUCT_INCLUDE(FInstanceCullingDrawParams, InstanceCullingDrawParams)
|
|
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()
|
|
|
|
FSceneUniformBuffer* FRendererModule::CreateSinglePrimitiveSceneUniformBuffer(FRDGBuilder& GraphBuilder, const FViewInfo& SceneView, FMeshBatch& Mesh)
|
|
{
|
|
return CreateSinglePrimitiveSceneUniformBuffer(GraphBuilder, SceneView.FeatureLevel, Mesh);
|
|
}
|
|
|
|
FSceneUniformBuffer* FRendererModule::CreateSinglePrimitiveSceneUniformBuffer(FRDGBuilder& GraphBuilder, ERHIFeatureLevel::Type FeatureLevel, FMeshBatch& Mesh)
|
|
{
|
|
FSceneUniformBuffer& SceneUniforms = *GraphBuilder.AllocObject<FSceneUniformBuffer>();
|
|
|
|
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 CreateSinglePrimitiveSceneUniformBuffer"));
|
|
checkf(MeshElement.PrimitiveUniformBuffer == nullptr, TEXT("CreateSinglePrimitiveSceneUniformBuffer 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("CreateSinglePrimitiveSceneUniformBuffer 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
|
|
FPrimitiveSceneShaderData PrimitiveSceneData(PrimitiveParams);
|
|
|
|
// Also fill out correct single-primitive instance data, derived from the primitive.
|
|
FInstanceSceneShaderData InstanceSceneData{};
|
|
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,
|
|
true,
|
|
FInstanceSceneShaderData::SupportsCompressedTransforms()
|
|
);
|
|
|
|
// Set up the parameters for the LightmapSceneData from the given LCI data
|
|
FPrecomputedLightingUniformParameters LightmapParams;
|
|
GetPrecomputedLightingParameters(FeatureLevel, LightmapParams, Mesh.LCI);
|
|
FLightmapSceneShaderData LightmapSceneData(LightmapParams);
|
|
|
|
FRDGBufferRef PrimitiveSceneDataBuffer = CreateStructuredBuffer(GraphBuilder, TEXT("PrimitiveSceneDataBuffer"), TConstArrayView<FVector4f>(PrimitiveSceneData.Data));
|
|
FRDGBufferRef LightmapSceneDataBuffer = CreateStructuredBuffer(GraphBuilder, TEXT("LightmapSceneDataBuffer"), TConstArrayView<FVector4f>(LightmapSceneData.Data));
|
|
FRDGBufferRef InstanceSceneDataBuffer = CreateStructuredBuffer(GraphBuilder, TEXT("InstanceSceneDataBuffer"), TConstArrayView<FVector4f>(InstanceSceneData.Data));
|
|
FRDGBufferRef InstancePayloadDataBuffer = GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(FVector4f));
|
|
FRDGBufferRef DummyBufferLight = GSystemTextures.GetDefaultByteAddressBuffer(GraphBuilder, sizeof(FLightSceneData));
|
|
|
|
FGPUSceneResourceParameters ShaderParameters;
|
|
ShaderParameters.GPUScenePrimitiveSceneData = GraphBuilder.CreateSRV(PrimitiveSceneDataBuffer);
|
|
ShaderParameters.GPUSceneInstanceSceneData = GraphBuilder.CreateSRV(InstanceSceneDataBuffer);
|
|
ShaderParameters.GPUSceneInstancePayloadData = GraphBuilder.CreateSRV(InstancePayloadDataBuffer);
|
|
ShaderParameters.GPUSceneLightmapData = GraphBuilder.CreateSRV(LightmapSceneDataBuffer);
|
|
ShaderParameters.GPUSceneLightData = GraphBuilder.CreateSRV(DummyBufferLight);
|
|
ShaderParameters.CommonParameters.GPUSceneMaxAllocatedInstanceId = 1;
|
|
ShaderParameters.CommonParameters.GPUSceneMaxPersistentPrimitiveIndex = 1;
|
|
ShaderParameters.CommonParameters.GPUSceneInstanceDataTileSizeLog2 = 0;
|
|
ShaderParameters.CommonParameters.GPUSceneInstanceDataTileSizeMask = 1;
|
|
ShaderParameters.CommonParameters.GPUSceneInstanceDataTileStride = 0;
|
|
|
|
SceneUniforms.Set(SceneUB::GPUScene, ShaderParameters);
|
|
}
|
|
}
|
|
|
|
return &SceneUniforms;
|
|
}
|
|
|
|
TRDGUniformBufferRef<FBatchedPrimitiveParameters> FRendererModule::CreateSinglePrimitiveUniformView(FRDGBuilder& GraphBuilder, const FViewInfo& SceneView, FMeshBatch& Mesh)
|
|
{
|
|
return CreateSinglePrimitiveUniformView(GraphBuilder, SceneView.FeatureLevel, SceneView.GetShaderPlatform(), Mesh);
|
|
}
|
|
|
|
TRDGUniformBufferRef<FBatchedPrimitiveParameters> FRendererModule::CreateSinglePrimitiveUniformView(FRDGBuilder& GraphBuilder, ERHIFeatureLevel::Type FeatureLevel, EShaderPlatform ShaderPlatform, FMeshBatch& Mesh)
|
|
{
|
|
check(PlatformGPUSceneUsesUniformBufferView(ShaderPlatform));
|
|
|
|
FBatchedPrimitiveParameters* BatchedPrimitiveParameters = GraphBuilder.AllocParameters<FBatchedPrimitiveParameters>();
|
|
|
|
FRDGBufferDesc PrimitiveDataBufferDesc = FRDGBufferDesc::CreateStructuredUploadDesc(16u, (PLATFORM_MAX_UNIFORM_BUFFER_RANGE / 16u));
|
|
PrimitiveDataBufferDesc.Usage |= EBufferUsageFlags::UniformBuffer;
|
|
FRDGBufferRef PrimitiveDataBuffer = nullptr;
|
|
|
|
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 CreateSinglePrimitiveUniformView"));
|
|
checkf(MeshElement.PrimitiveUniformBuffer == nullptr, TEXT("CreateSinglePrimitiveUniformView 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("CreateSinglePrimitiveUniformView does not currently support instancing"));
|
|
// Force PrimitiveId to be 0 in the shader
|
|
MeshElement.PrimitiveIdMode = PrimID_ForceZero;
|
|
FPrimitiveUniformShaderParameters PrimitiveParams = *(const FPrimitiveUniformShaderParameters*)MeshElement.PrimitiveUniformBufferResource->GetContents();
|
|
// Now we just need to fill out the first entry of a batched primitive data in a buffer
|
|
FBatchedPrimitiveShaderData ShaderData(PrimitiveParams);
|
|
PrimitiveDataBuffer = GraphBuilder.CreateBuffer(PrimitiveDataBufferDesc, TEXT("SinglePrimitiveUniformView"));
|
|
GraphBuilder.QueueBufferUpload(PrimitiveDataBuffer, ShaderData.Data.GetData(), ShaderData.Data.Num() * sizeof(FVector4f));
|
|
}
|
|
}
|
|
|
|
if (PrimitiveDataBuffer == nullptr)
|
|
{
|
|
// Upload Identity parameters
|
|
FBatchedPrimitiveShaderData ShaderData{};
|
|
PrimitiveDataBuffer = GraphBuilder.CreateBuffer(PrimitiveDataBufferDesc, TEXT("SinglePrimitiveUniformView"));
|
|
GraphBuilder.QueueBufferUpload(PrimitiveDataBuffer, ShaderData.Data.GetData(), ShaderData.Data.Num() * sizeof(FVector4f));
|
|
}
|
|
|
|
BatchedPrimitiveParameters->Data = GraphBuilder.CreateSRV(PrimitiveDataBuffer);
|
|
return GraphBuilder.CreateUniformBuffer(BatchedPrimitiveParameters);
|
|
}
|
|
|
|
static float GetEmissiveMaxValueForPixelFormat(EPixelFormat PixelFormat)
|
|
{
|
|
switch (PixelFormat)
|
|
{
|
|
// R11G11B10
|
|
case PF_FloatR11G11B10:
|
|
case PF_FloatRGB:
|
|
return 64512.0f; // Max10BitsFloat
|
|
|
|
// FP16
|
|
case PF_FloatRGBA:
|
|
case PF_G16R16F:
|
|
case PF_G16R16F_FILTER:
|
|
case PF_R16F:
|
|
case PF_R16F_FILTER:
|
|
return FFloat16::MaxF16Float;
|
|
|
|
// FP32
|
|
//case PF_R32_FLOAT:
|
|
//case PF_G32R32F:
|
|
//case PF_A32B32G32R32F:
|
|
//default:
|
|
// fall through
|
|
}
|
|
|
|
return UE_MAX_FLT; // default for FP32 and all other formats for now
|
|
}
|
|
|
|
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;
|
|
FViewFamilyInfo* ViewFamily = RenderContext.Alloc<FViewFamilyInfo>(*SceneView.Family);
|
|
ViewFamily->Views.Add(&View);
|
|
ViewFamily->AllViews.Add(&View);
|
|
View.Family = ViewFamily;
|
|
|
|
// When rendering tiles, this may be to render data in a URenderTargetTexture. In this case we should not clamp so that all the expected values setup by the artists go through.
|
|
if (RenderContext.GetRenderTarget())
|
|
{
|
|
View.MaterialMaxEmissiveValue = GetEmissiveMaxValueForPixelFormat(RenderContext.GetRenderTarget()->Desc.Format);
|
|
}
|
|
|
|
// Default init of SceneTexturesConfig will take extents from FSceneTextureExtentState. We want the view extents, so explicitly
|
|
// set that. This will bypass scene texture extent caching logic, but this code path doesn't allocate scene textures (it renders
|
|
// directly to RenderContext.GetRenderTarget()), so caching is irrelevant for purposes of avoiding render target pool thrashing.
|
|
InitializeSceneTexturesConfig(ViewFamily->SceneTexturesConfig, *ViewFamily, View.ViewRect.Size());
|
|
|
|
const auto FeatureLevel = View.GetFeatureLevel();
|
|
const EShadingPath ShadingPath = GetFeatureLevelShadingPath(FeatureLevel);
|
|
|
|
FScene* Scene = ViewFamily->Scene ? ViewFamily->Scene->GetRenderScene() : nullptr;
|
|
|
|
Mesh.MaterialRenderProxy->UpdateUniformExpressionCacheIfNeeded(FeatureLevel);
|
|
FMaterialRenderProxy::UpdateDeferredCachedUniformExpressions();
|
|
|
|
FRDGBuilder& GraphBuilder = RenderContext.GraphBuilder;
|
|
|
|
FSceneUniformBuffer& SceneUniforms = *CreateSinglePrimitiveSceneUniformBuffer(GraphBuilder, FeatureLevel, Mesh);
|
|
|
|
if (!FRDGSystemTextures::IsValid(GraphBuilder))
|
|
{
|
|
FRDGSystemTextures::Create(GraphBuilder);
|
|
}
|
|
|
|
// Materials sampling VTs need FVirtualTextureSystem to be updated before being rendered.
|
|
// Note that we any VTs dependent on having a SceneRenderer (eg RVT) cannot be warmed up here.
|
|
const FMaterial& MeshMaterial = Mesh.MaterialRenderProxy->GetIncompleteMaterialWithFallback(FeatureLevel);
|
|
const bool bUseVirtualTexturing = UseVirtualTexturing(View.GetShaderPlatform()) && !MeshMaterial.GetUniformVirtualTextureExpressions().IsEmpty();
|
|
if (bUseVirtualTexturing)
|
|
{
|
|
FVirtualTextureUpdateSettings Settings;
|
|
Settings.EnableThrottling(false);
|
|
FVirtualTextureSystem::Get().Update(GraphBuilder, FeatureLevel, nullptr, Settings);
|
|
|
|
VirtualTextureFeedbackBegin(GraphBuilder, TArrayView<const FViewInfo>(&View, 1), RenderContext.GetViewportRect().Size());
|
|
}
|
|
|
|
View.InitRHIResources();
|
|
View.ForwardLightingResources.SetUniformBuffer(CreateDummyForwardLightUniformBuffer(GraphBuilder, View.GetShaderPlatform()));
|
|
SetDummyLocalFogVolumeForView(GraphBuilder, View);
|
|
|
|
// Create a disabled LightFunctionAtlas to be able to render base pass.
|
|
LightFunctionAtlas::FLightFunctionAtlas LightFunctionAtlas;
|
|
LightFunctionAtlas::FLightFunctionAtlasSceneData LightFunctionAtlasSceneData;
|
|
LightFunctionAtlas.ClearEmptySceneFrame(&View, 0u, &LightFunctionAtlasSceneData);
|
|
|
|
TUniformBufferRef<FReflectionCaptureShaderData> EmptyReflectionCaptureUniformBuffer;
|
|
|
|
{
|
|
FReflectionCaptureShaderData EmptyData;
|
|
EmptyReflectionCaptureUniformBuffer = TUniformBufferRef<FReflectionCaptureShaderData>::CreateUniformBufferImmediate(EmptyData, UniformBuffer_SingleFrame);
|
|
}
|
|
|
|
RDG_EVENT_SCOPE(GraphBuilder, "DrawTileMesh");
|
|
|
|
auto* PassParameters = GraphBuilder.AllocParameters<FDrawTileMeshPassParameters>();
|
|
PassParameters->RenderTargets[0] = FRenderTargetBinding(RenderContext.GetRenderTarget(), ERenderTargetLoadAction::ELoad);
|
|
PassParameters->View = View.GetShaderParameters();
|
|
PassParameters->InstanceCullingDrawParams.Scene = SceneUniforms.GetBuffer(GraphBuilder);
|
|
PassParameters->InstanceCullingDrawParams.InstanceCulling = FInstanceCullingContext::CreateDummyInstanceCullingUniformBuffer(GraphBuilder);
|
|
PassParameters->ReflectionCapture = EmptyReflectionCaptureUniformBuffer;
|
|
|
|
// FORT-702555, FORT-819709 It is done to workaround a GLES driver bug on old Adreno 6xx drivers.
|
|
if (IsAndroidOpenGLESPlatform(ViewFamily->GetShaderPlatform()) && bBindTileMeshDrawingDummyRenderTarget)
|
|
{
|
|
// Unfortunately, this unused render-target should match the size of actually
|
|
// used one to be GL spec conformant. It states the following:
|
|
// "If the attachment sizes are not all identical, the results of rendering are de-
|
|
// fined only within the largest area that can fit in all of the attachments."
|
|
// Hence, this fix hogs some memory.
|
|
FSceneTexturesConfig& Config = ViewFamily->SceneTexturesConfig;
|
|
FRDGTextureDesc RenderTargetDesc = FRDGTextureDesc::Create2D(Config.Extent, PF_R16F, FClearValueBinding(FLinearColor::Transparent), TexCreate_RenderTargetable | TexCreate_ShaderResource | TexCreate_InputAttachmentRead);
|
|
FRDGTextureRef DummyRenderTarget = GraphBuilder.CreateTexture(RenderTargetDesc, TEXT("DummyTileMeshRT"));
|
|
PassParameters->RenderTargets[1] = FRenderTargetBinding(DummyRenderTarget, ERenderTargetLoadAction::ENoAction);
|
|
}
|
|
|
|
// Disable parallel setup tasks since we are only processing one mesh (not worth the task launch cost).
|
|
const bool bForceStereoInstancingOff = false;
|
|
const bool bForceParallelSetupOff = true;
|
|
|
|
// 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);
|
|
|
|
AddDrawDynamicMeshPass(GraphBuilder, RDG_EVENT_NAME("OutputMaterialTextureScales"), PassParameters, View, RenderContext.GetViewportRect(), RenderContext.GetScissorRect(),
|
|
[Scene, &View, &Mesh](FMeshPassDrawListContext* InDrawListContext)
|
|
{
|
|
FDebugViewModeMeshProcessor PassMeshProcessor(
|
|
Scene,
|
|
View.GetFeatureLevel(),
|
|
&View,
|
|
false,
|
|
InDrawListContext);
|
|
const uint64 DefaultBatchElementMask = ~0ull;
|
|
PassMeshProcessor.AddMeshBatch(Mesh, DefaultBatchElementMask, nullptr);
|
|
|
|
}, bForceStereoInstancingOff, bForceParallelSetupOff);
|
|
}
|
|
#endif // WITH_DEBUG_VIEW_MODES
|
|
}
|
|
else if (IsTranslucentBlendMode(MeshMaterial))
|
|
{
|
|
if (ShadingPath == EShadingPath::Deferred)
|
|
{
|
|
PassParameters->TranslucentBasePass = CreateTranslucentBasePassUniformBuffer(GraphBuilder, Scene, View);
|
|
|
|
AddDrawDynamicMeshPass(GraphBuilder, RDG_EVENT_NAME("TranslucentDeferred"), PassParameters, View, RenderContext.GetViewportRect(), RenderContext.GetScissorRect(),
|
|
[Scene, &View, &Mesh, DrawRenderState, bUse128bitRT](FMeshPassDrawListContext* DynamicMeshPassContext)
|
|
{
|
|
FBasePassMeshProcessor PassMeshProcessor(
|
|
EMeshPass::BasePass,
|
|
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);
|
|
}, bForceStereoInstancingOff, bForceParallelSetupOff);
|
|
}
|
|
else // Mobile
|
|
{
|
|
PassParameters->MobileBasePass = CreateMobileBasePassUniformBuffer(GraphBuilder, View, EMobileBasePass::Translucent, EMobileSceneTextureSetupMode::None);
|
|
|
|
AddDrawDynamicMeshPass(GraphBuilder, RDG_EVENT_NAME("TranslucentMobile"), PassParameters, View, RenderContext.GetViewportRect(), RenderContext.GetScissorRect(),
|
|
[Scene, &View, DrawRenderState, &Mesh](FDynamicPassMeshDrawListContext* DynamicMeshPassContext)
|
|
{
|
|
FMobileBasePassMeshProcessor PassMeshProcessor(
|
|
EMeshPass::TranslucencyAll,
|
|
Scene,
|
|
&View,
|
|
DrawRenderState,
|
|
DynamicMeshPassContext,
|
|
FMobileBasePassMeshProcessor::EFlags::None,
|
|
ETranslucencyPass::TPT_AllTranslucency);
|
|
|
|
const uint64 DefaultBatchElementMask = ~0ull;
|
|
PassMeshProcessor.AddMeshBatch(Mesh, DefaultBatchElementMask, nullptr);
|
|
}, bForceStereoInstancingOff, bForceParallelSetupOff);
|
|
}
|
|
}
|
|
// 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
|
|
AddDrawDynamicMeshPass(GraphBuilder, RDG_EVENT_NAME("HitTesting"), PassParameters, View, RenderContext.GetViewportRect(), RenderContext.GetScissorRect(),
|
|
[Scene, &View, DrawRenderState, &Mesh](FDynamicPassMeshDrawListContext* DynamicMeshPassContext)
|
|
{
|
|
FHitProxyMeshProcessor PassMeshProcessor(
|
|
Scene,
|
|
&View,
|
|
false,
|
|
DrawRenderState,
|
|
DynamicMeshPassContext);
|
|
|
|
const uint64 DefaultBatchElementMask = ~0ull;
|
|
PassMeshProcessor.AddMeshBatch(Mesh, DefaultBatchElementMask, nullptr);
|
|
}, bForceStereoInstancingOff, bForceParallelSetupOff);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
if (ShadingPath == EShadingPath::Deferred)
|
|
{
|
|
PassParameters->OpaqueBasePass = CreateOpaqueBasePassUniformBuffer(GraphBuilder, View);
|
|
|
|
AddDrawDynamicMeshPass(GraphBuilder, RDG_EVENT_NAME("OpaqueDeferred"), PassParameters, View, RenderContext.GetViewportRect(), RenderContext.GetScissorRect(),
|
|
[Scene, &View, DrawRenderState, &Mesh, bUse128bitRT](FDynamicPassMeshDrawListContext* DynamicMeshPassContext)
|
|
{
|
|
FBasePassMeshProcessor PassMeshProcessor(
|
|
EMeshPass::BasePass,
|
|
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);
|
|
|
|
AddDrawDynamicMeshPass(GraphBuilder, RDG_EVENT_NAME("OpaqueMobile"), PassParameters, View, RenderContext.GetViewportRect(), RenderContext.GetScissorRect(),
|
|
[Scene, &View, DrawRenderState, &Mesh](FDynamicPassMeshDrawListContext* DynamicMeshPassContext)
|
|
{
|
|
FMobileBasePassMeshProcessor PassMeshProcessor(
|
|
EMeshPass::BasePass,
|
|
Scene,
|
|
&View,
|
|
DrawRenderState,
|
|
DynamicMeshPassContext,
|
|
FMobileBasePassMeshProcessor::EFlags::CanReceiveCSM | FMobileBasePassMeshProcessor::EFlags::ForcePassDrawRenderState);
|
|
|
|
const uint64 DefaultBatchElementMask = ~0ull;
|
|
PassMeshProcessor.AddMeshBatch(Mesh, DefaultBatchElementMask, nullptr);
|
|
}, bForceStereoInstancingOff, bForceParallelSetupOff);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bUseVirtualTexturing)
|
|
{
|
|
VirtualTexture::EndFeedback(GraphBuilder);
|
|
}
|
|
}
|
|
}
|
|
|
|
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(-UE_OLD_WORLD_MAX), FVector(+UE_OLD_WORLD_MAX)); // LWC_TODO: Scale to renderable world bounds?
|
|
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 FVector::FReal ZOffset = UE_OLD_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();
|
|
}
|
|
|
|
void FRendererModule::ResetSceneTextureExtentHistory()
|
|
{
|
|
::ResetSceneTextureExtentHistory();
|
|
}
|
|
|
|
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
|
|
}
|