You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
437 lines
13 KiB
C++
437 lines
13 KiB
C++
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
Renderer.cpp: Renderer module implementation.
|
|
=============================================================================*/
|
|
|
|
#include "RendererPrivate.h"
|
|
#include "ScenePrivate.h"
|
|
#include "AssertionMacros.h"
|
|
#include "GPUBenchmark.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
|
|
|
|
void FRendererModule::ReallocateSceneRenderTargets()
|
|
{
|
|
FLightPrimitiveInteraction::InitializeMemoryPool();
|
|
GSceneRenderTargets.UpdateRHI();
|
|
}
|
|
|
|
void FRendererModule::SceneRenderTargetsSetBufferSize(uint32 SizeX, uint32 SizeY)
|
|
{
|
|
GSceneRenderTargets.SetBufferSize(SizeX, SizeY);
|
|
GSceneRenderTargets.UpdateRHI();
|
|
}
|
|
|
|
void FRendererModule::DrawTileMesh(FRHICommandListImmediate& RHICmdList, const FSceneView& SceneView, const FMeshBatch& Mesh, bool bIsHitTesting, const FHitProxyId& HitProxyId)
|
|
{
|
|
// 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(&SceneView);
|
|
View.InitRHIResources(nullptr);
|
|
|
|
const auto FeatureLevel = View.GetFeatureLevel();
|
|
|
|
const FMaterial* Material = Mesh.MaterialRenderProxy->GetMaterial(FeatureLevel);
|
|
|
|
//get the blend mode of the material
|
|
const EBlendMode MaterialBlendMode = Material->GetBlendMode();
|
|
|
|
if (!GUsingNullRHI)
|
|
{
|
|
// handle translucent material blend modes
|
|
if (IsTranslucentBlendMode(MaterialBlendMode))
|
|
{
|
|
if (FeatureLevel >= ERHIFeatureLevel::SM4)
|
|
{
|
|
FTranslucencyDrawingPolicyFactory::DrawDynamicMesh(RHICmdList, View, FTranslucencyDrawingPolicyFactory::ContextType(), Mesh, false, false, NULL, HitProxyId);
|
|
}
|
|
else
|
|
{
|
|
FTranslucencyForwardShadingDrawingPolicyFactory::DrawDynamicMesh(RHICmdList, View, FTranslucencyForwardShadingDrawingPolicyFactory::ContextType(), Mesh, false, false, NULL, HitProxyId);
|
|
}
|
|
}
|
|
// handle opaque materials
|
|
else
|
|
{
|
|
// make sure we are doing opaque drawing
|
|
RHICmdList.SetBlendState(TStaticBlendState<>::GetRHI());
|
|
|
|
// draw the mesh
|
|
if (bIsHitTesting)
|
|
{
|
|
FHitProxyDrawingPolicyFactory::DrawDynamicMesh(RHICmdList, View, FHitProxyDrawingPolicyFactory::ContextType(), Mesh, false, false, NULL, HitProxyId);
|
|
}
|
|
else
|
|
{
|
|
if (FeatureLevel >= ERHIFeatureLevel::SM4)
|
|
{
|
|
FBasePassOpaqueDrawingPolicyFactory::DrawDynamicMesh(RHICmdList, View, FBasePassOpaqueDrawingPolicyFactory::ContextType(false, ESceneRenderTargetsMode::SetTextures), Mesh, false, false, NULL, HitProxyId);
|
|
}
|
|
else
|
|
{
|
|
FBasePassForwardOpaqueDrawingPolicyFactory::DrawDynamicMesh(RHICmdList, View, FBasePassForwardOpaqueDrawingPolicyFactory::ContextType(false, ESceneRenderTargetsMode::SetTextures), Mesh, false, false, NULL, HitProxyId);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FRendererModule::RenderTargetPoolFindFreeElement(const FPooledRenderTargetDesc& Desc, TRefCountPtr<IPooledRenderTarget> &Out, const TCHAR* InDebugName)
|
|
{
|
|
GRenderTargetPool.FindFreeElement(Desc, Out, InDebugName);
|
|
}
|
|
|
|
void FRendererModule::TickRenderTargetPool()
|
|
{
|
|
GRenderTargetPool.TickPoolElements();
|
|
}
|
|
|
|
void FRendererModule::DebugLogOnCrash()
|
|
{
|
|
GRenderTargetPool.VisualizeTexture.SortOrder = 1;
|
|
GRenderTargetPool.VisualizeTexture.bFullList = true;
|
|
GRenderTargetPool.VisualizeTexture.DebugLog(false);
|
|
|
|
// execute on main thread
|
|
{
|
|
struct FTest
|
|
{
|
|
void Thread()
|
|
{
|
|
GEngine->Exec(NULL, TEXT("Mem FromReport"), *GLog);
|
|
GEngine->Exec(NULL, TEXT("rhi.DumpMemory"), *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);
|
|
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_THREEPARAMETER(
|
|
RendererGPUBenchmarkCommand,
|
|
FSceneView, DummyView, DummyView,
|
|
float, WorkScale, WorkScale,
|
|
FSynthBenchmarkResults&, InOut, InOut,
|
|
{
|
|
RendererGPUBenchmark(RHICmdList, InOut, DummyView, WorkScale);
|
|
});
|
|
FlushRenderingCommands();
|
|
}
|
|
|
|
void FRendererModule::QueryVisualizeTexture(FQueryVisualizeTexureInfo& Out)
|
|
{
|
|
check(IsInGameThread());
|
|
FlushRenderingCommands();
|
|
|
|
GRenderTargetPool.VisualizeTexture.QueryInfo(Out);
|
|
}
|
|
|
|
static void VisualizeTextureExec( const TCHAR* Cmd, FOutputDevice &Ar )
|
|
{
|
|
check(IsInGameThread());
|
|
|
|
FlushRenderingCommands();
|
|
|
|
uint32 ParameterCount = 0;
|
|
|
|
// parse parameters
|
|
for(;;)
|
|
{
|
|
FString Parameter = FParse::Token(Cmd, 0);
|
|
|
|
if(Parameter.IsEmpty())
|
|
{
|
|
break;
|
|
}
|
|
|
|
Parameter.ToLower();
|
|
|
|
// FULL flag
|
|
if(Parameter == TEXT("fulllist") || Parameter == TEXT("full"))
|
|
{
|
|
GRenderTargetPool.VisualizeTexture.bFullList = true;
|
|
// this one doesn't count as parameter so we can do "vis full"
|
|
continue;
|
|
}
|
|
// SORT0 flag
|
|
else if(Parameter == TEXT("sort0"))
|
|
{
|
|
GRenderTargetPool.VisualizeTexture.SortOrder = 0;
|
|
// this one doesn't count as parameter so we can do "vis full"
|
|
continue;
|
|
}
|
|
// SORT1 flag
|
|
else if(Parameter == TEXT("sort1"))
|
|
{
|
|
GRenderTargetPool.VisualizeTexture.SortOrder = 1;
|
|
// this one doesn't count as parameter so we can do "vis full"
|
|
continue;
|
|
}
|
|
else if(ParameterCount == 0)
|
|
{
|
|
// Init
|
|
GRenderTargetPool.VisualizeTexture.RGBMul = 1;
|
|
GRenderTargetPool.VisualizeTexture.AMul = 0;
|
|
GRenderTargetPool.VisualizeTexture.UVInputMapping = 3;
|
|
GRenderTargetPool.VisualizeTexture.Flags = 0;
|
|
GRenderTargetPool.VisualizeTexture.Mode = 0;
|
|
GRenderTargetPool.VisualizeTexture.CustomMip = 0;
|
|
GRenderTargetPool.VisualizeTexture.ArrayIndex = 0;
|
|
GRenderTargetPool.VisualizeTexture.bOutputStencil = false;
|
|
|
|
// e.g. "VisualizeTexture Name" or "VisualizeTexture 5"
|
|
bool bIsDigit = FChar::IsDigit(**Parameter);
|
|
|
|
if (bIsDigit)
|
|
{
|
|
GRenderTargetPool.VisualizeTexture.Mode = FCString::Atoi(*Parameter);
|
|
}
|
|
|
|
if(!bIsDigit)
|
|
{
|
|
// the name was specified as string
|
|
const TCHAR* AfterAt = *Parameter;
|
|
|
|
while(*AfterAt != 0 && *AfterAt != TCHAR('@'))
|
|
{
|
|
++AfterAt;
|
|
}
|
|
|
|
if(*AfterAt == TCHAR('@'))
|
|
{
|
|
// user specified a reuse goal
|
|
FString NameWithoutAt = Parameter.Left(AfterAt - *Parameter);
|
|
GRenderTargetPool.VisualizeTexture.SetObserveTarget(*NameWithoutAt, FCString::Atoi(AfterAt + 1));
|
|
}
|
|
else
|
|
{
|
|
// we take the last one
|
|
GRenderTargetPool.VisualizeTexture.SetObserveTarget(*Parameter);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// the index was used
|
|
GRenderTargetPool.VisualizeTexture.SetObserveTarget(TEXT(""));
|
|
}
|
|
}
|
|
// GRenderTargetPoolInputMapping mode
|
|
else if(Parameter == TEXT("uv0"))
|
|
{
|
|
GRenderTargetPool.VisualizeTexture.UVInputMapping = 0;
|
|
}
|
|
else if(Parameter == TEXT("uv1"))
|
|
{
|
|
GRenderTargetPool.VisualizeTexture.UVInputMapping = 1;
|
|
}
|
|
else if(Parameter == TEXT("uv2"))
|
|
{
|
|
GRenderTargetPool.VisualizeTexture.UVInputMapping = 2;
|
|
}
|
|
else if(Parameter == TEXT("pip"))
|
|
{
|
|
GRenderTargetPool.VisualizeTexture.UVInputMapping = 3;
|
|
}
|
|
// BMP flag
|
|
else if(Parameter == TEXT("bmp"))
|
|
{
|
|
GRenderTargetPool.VisualizeTexture.bSaveBitmap = true;
|
|
}
|
|
else if (Parameter == TEXT("stencil"))
|
|
{
|
|
GRenderTargetPool.VisualizeTexture.bOutputStencil = true;
|
|
}
|
|
// saturate flag
|
|
else if(Parameter == TEXT("frac"))
|
|
{
|
|
// default already covers this
|
|
}
|
|
// saturate flag
|
|
else if(Parameter == TEXT("sat"))
|
|
{
|
|
GRenderTargetPool.VisualizeTexture.Flags |= 0x1;
|
|
}
|
|
// e.g. mip2 or mip0
|
|
else if(Parameter.Left(3) == TEXT("mip"))
|
|
{
|
|
Parameter = Parameter.Right(Parameter.Len() - 3);
|
|
GRenderTargetPool.VisualizeTexture.CustomMip = FCString::Atoi(*Parameter);
|
|
}
|
|
// e.g. [0] or [2]
|
|
else if(Parameter.Left(5) == TEXT("index"))
|
|
{
|
|
Parameter = Parameter.Right(Parameter.Len() - 5);
|
|
GRenderTargetPool.VisualizeTexture.ArrayIndex = FCString::Atoi(*Parameter);
|
|
}
|
|
// e.g. RGB*6, A, *22, /2.7, A*7
|
|
else if(Parameter.Left(3) == TEXT("rgb")
|
|
|| Parameter.Left(1) == TEXT("a")
|
|
|| Parameter.Left(1) == TEXT("*")
|
|
|| Parameter.Left(1) == TEXT("/"))
|
|
{
|
|
if(Parameter.Left(3) == TEXT("rgb"))
|
|
{
|
|
Parameter = Parameter.Right(Parameter.Len() - 3);
|
|
}
|
|
else if(Parameter.Left(1) == TEXT("a"))
|
|
{
|
|
Parameter = Parameter.Right(Parameter.Len() - 1);
|
|
GRenderTargetPool.VisualizeTexture.RGBMul = 0;
|
|
GRenderTargetPool.VisualizeTexture.AMul = 1;
|
|
}
|
|
|
|
float Mul = 1.0f;
|
|
|
|
// * or /
|
|
if(Parameter.Left(1) == TEXT("*"))
|
|
{
|
|
Parameter = Parameter.Right(Parameter.Len() - 1);
|
|
Mul = FCString::Atof(*Parameter);
|
|
}
|
|
else if(Parameter.Left(1) == TEXT("/"))
|
|
{
|
|
Parameter = Parameter.Right(Parameter.Len() - 1);
|
|
Mul = 1.0f / FCString::Atof(*Parameter);
|
|
}
|
|
GRenderTargetPool.VisualizeTexture.RGBMul *= Mul;
|
|
GRenderTargetPool.VisualizeTexture.AMul *= Mul;
|
|
}
|
|
else
|
|
{
|
|
Ar.Logf(TEXT("Error: parameter \"%s\" not recognized"), *Parameter);
|
|
}
|
|
|
|
++ParameterCount;
|
|
}
|
|
|
|
if(!ParameterCount)
|
|
{
|
|
// show help
|
|
Ar.Logf(TEXT("VisualizeTexture/Vis <TextureId/CheckpointName> [<Mode>] [PIP/UV0/UV1/UV2] [BMP] [FRAC/SAT] [FULL]:"));
|
|
|
|
Ar.Logf(TEXT("Mode (examples):"));
|
|
Ar.Logf(TEXT(" RGB = RGB in range 0..1 (default)"));
|
|
Ar.Logf(TEXT(" *8 = RGB * 8"));
|
|
Ar.Logf(TEXT(" A = alpha channel in range 0..1"));
|
|
Ar.Logf(TEXT(" A*16 = Alpha * 16"));
|
|
Ar.Logf(TEXT(" RGB/2 = RGB / 2"));
|
|
Ar.Logf(TEXT("SubResource:"));
|
|
Ar.Logf(TEXT(" MIP5 = Mip level 5 (0 is default)"));
|
|
Ar.Logf(TEXT(" INDEX5 = Array Element 5 (0 is default)"));
|
|
Ar.Logf(TEXT("InputMapping:"));
|
|
Ar.Logf(TEXT(" PIP = like UV1 but as picture in picture with normal rendering (default)"));
|
|
Ar.Logf(TEXT(" UV0 = UV in left top"));
|
|
Ar.Logf(TEXT(" UV1 = full texture"));
|
|
Ar.Logf(TEXT(" UV2 = pixel perfect centered"));
|
|
Ar.Logf(TEXT("Flags:"));
|
|
Ar.Logf(TEXT(" BMP = save out bitmap to the screenshots folder (not on console, normalized)"));
|
|
Ar.Logf(TEXT("STENCIL = Stencil normally displayed in alpha channel of depth. This option is used for BMP to get a stencil only BMP."));
|
|
Ar.Logf(TEXT(" FRAC = use frac() in shader (default)"));
|
|
Ar.Logf(TEXT(" SAT = use saturate() in shader"));
|
|
Ar.Logf(TEXT(" FULLLIST = show full list, otherwise we hide some textures in the printout"));
|
|
Ar.Logf(TEXT(" SORT0 = sort list by name"));
|
|
Ar.Logf(TEXT(" SORT1 = show list by size"));
|
|
Ar.Logf(TEXT("TextureId:"));
|
|
Ar.Logf(TEXT(" 0 = <off>"));
|
|
|
|
GRenderTargetPool.VisualizeTexture.DebugLog(true);
|
|
}
|
|
// Ar.Logf(TEXT("VisualizeTexture %d"), GRenderTargetPool.VisualizeTexture.Mode);
|
|
}
|
|
|
|
static bool RendererExec( UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar )
|
|
{
|
|
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
if (FParse::Command(&Cmd, TEXT("VisualizeTexture")) || FParse::Command(&Cmd, TEXT("Vis")))
|
|
{
|
|
VisualizeTextureExec(Cmd, Ar);
|
|
return true;
|
|
}
|
|
else if (FParse::Command(&Cmd, TEXT("ShowMipLevels")))
|
|
{
|
|
extern bool GVisualizeMipLevels;
|
|
GVisualizeMipLevels = !GVisualizeMipLevels;
|
|
Ar.Logf( TEXT( "Showing mip levels: %s" ), GVisualizeMipLevels ? TEXT("ENABLED") : TEXT("DISABLED") );
|
|
return true;
|
|
}
|
|
else if(FParse::Command(&Cmd,TEXT("DumpUnbuiltLightInteractions")))
|
|
{
|
|
InWorld->Scene->DumpUnbuiltLightIteractions(Ar);
|
|
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
|
|
}
|