Files
UnrealEngineUWP/Engine/Source/Runtime/Renderer/Private/TiledDeferredLightRendering.cpp
charles derousiers 24fea8db34 Change LTC textures to be stored onto the view to avoid manual binding into the several passes.
#rb sebastien.hillaire
#jira none
#preflight 61a7598f800738dbfba11678

#ROBOMERGE-AUTHOR: charles.derousiers
#ROBOMERGE-SOURCE: CL 18337660 in //UE5/Release-5.0/... via CL 18337678
#ROBOMERGE-BOT: STARSHIP (Release-Engine-Staging -> Release-Engine-Test) (v895-18170469)

[CL 18337679 by charles derousiers in ue5-release-engine-test branch]
2021-12-01 07:03:05 -05:00

366 lines
16 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
TiledDeferredLightRendering.cpp: Implementation of tiled deferred shading
=============================================================================*/
#include "CoreMinimal.h"
#include "Stats/Stats.h"
#include "HAL/IConsoleManager.h"
#include "EngineGlobals.h"
#include "RHI.h"
#include "UniformBuffer.h"
#include "ShaderParameters.h"
#include "RendererInterface.h"
#include "Shader.h"
#include "SceneUtils.h"
#include "RHIStaticStates.h"
#include "PostProcess/SceneRenderTargets.h"
#include "LightSceneInfo.h"
#include "GlobalShader.h"
#include "SceneRenderTargetParameters.h"
#include "DeferredShadingRenderer.h"
#include "ScenePrivate.h"
/**
* Maximum number of lights that can be handled by tiled deferred in a single compute shader pass.
* If the scene has more visible lights than this, multiple tiled deferred passes will be needed which incurs the tile setup multiple times.
* This is currently limited by the size of the light constant buffers.
*/
static const int32 GMaxNumTiledDeferredLights = 1024;
/**
* Tile size for the deferred light compute shader. Larger tiles have more threads in flight, but less accurate culling.
* Tweaked for ~200 onscreen lights on a 7970.
* Changing this requires touching the shader to cause a recompile.
*/
const int32 GDeferredLightTileSizeX = 16;
const int32 GDeferredLightTileSizeY = 16;
int32 GUseTiledDeferredShading = 1;
static FAutoConsoleVariableRef CVarUseTiledDeferredShading(
TEXT("r.TiledDeferredShading"),
GUseTiledDeferredShading,
TEXT("Whether to use tiled deferred shading. 0 is off, 1 is on (default)"),
ECVF_RenderThreadSafe
);
// Tiled deferred has fixed overhead due to tile setup, but scales better than standard deferred
int32 GNumLightsBeforeUsingTiledDeferred = 80;
static FAutoConsoleVariableRef CVarNumLightsBeforeUsingTiledDeferred(
TEXT("r.TiledDeferredShading.MinimumCount"),
GNumLightsBeforeUsingTiledDeferred,
TEXT("Number of applicable lights that must be on screen before switching to tiled deferred.\n")
TEXT("0 means all lights that qualify (e.g. no shadows, ...) are rendered tiled deferred. Default: 80"),
ECVF_RenderThreadSafe
);
/**
* First constant buffer of light data for tiled deferred.
* Light data is split into two constant buffers to allow more lights per pass before hitting the d3d11 max constant buffer size of 4096 float4's
*/
BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FTiledDeferredLightData,)
SHADER_PARAMETER_ARRAY(FVector4f,LightPositionAndInvRadius,[GMaxNumTiledDeferredLights])
SHADER_PARAMETER_ARRAY(FVector4f,LightColorAndFalloffExponent,[GMaxNumTiledDeferredLights])
END_GLOBAL_SHADER_PARAMETER_STRUCT()
IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT(FTiledDeferredLightData,"TiledDeferred");
/** Second constant buffer of light data for tiled deferred. */
BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FTiledDeferredLightData2,)
SHADER_PARAMETER_ARRAY(FVector4f,LightDirectionAndSpotlightMaskAndSpecularScale,[GMaxNumTiledDeferredLights])
SHADER_PARAMETER_ARRAY(FVector4f,SpotAnglesAndSourceRadiusAndSimpleLighting,[GMaxNumTiledDeferredLights])
SHADER_PARAMETER_ARRAY(FVector4f,ShadowMapChannelMask,[GMaxNumTiledDeferredLights])
END_GLOBAL_SHADER_PARAMETER_STRUCT()
IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT(FTiledDeferredLightData2,"TiledDeferred2");
/** Compute shader used to implement tiled deferred lighting. */
template <bool bVisualizeLightCulling>
class FTiledDeferredLightingCS : public FGlobalShader
{
DECLARE_SHADER_TYPE(FTiledDeferredLightingCS,Global)
public:
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEX"), GDeferredLightTileSizeX);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEY"), GDeferredLightTileSizeY);
OutEnvironment.SetDefine(TEXT("MAX_LIGHTS"), GMaxNumTiledDeferredLights);
OutEnvironment.SetDefine(TEXT("VISUALIZE_LIGHT_CULLING"), (uint32)bVisualizeLightCulling);
// To reduce shader compile time of compute shaders with shared memory, doesn't have an impact on generated code with current compiler (June 2010 DX SDK)
OutEnvironment.CompilerFlags.Add(CFLAG_StandardOptimization);
}
FTiledDeferredLightingCS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer)
{
InTexture.Bind(Initializer.ParameterMap, TEXT("InTexture"));
OutTexture.Bind(Initializer.ParameterMap, TEXT("OutTexture"));
NumLights.Bind(Initializer.ParameterMap, TEXT("NumLights"));
ViewDimensions.Bind(Initializer.ParameterMap, TEXT("ViewDimensions"));
}
FTiledDeferredLightingCS()
{
}
CA_SUPPRESS(6262);
void SetParameters(
FRHIComputeCommandList& RHICmdList,
const FViewInfo& View,
int32 ViewIndex,
int32 NumViews,
const TArray<FSortedLightSceneInfo, SceneRenderingAllocator>& SortedLights,
int32 TiledDeferredLightsStart,
int32 TiledDeferredLightsEnd,
const FSimpleLightArray& SimpleLights,
int32 StartIndex,
int32 NumThisPass,
FRHITexture* InTextureValue,
FRHIUnorderedAccessView* OutTextureValue)
{
FRHIComputeShader* ShaderRHI = RHICmdList.GetBoundComputeShader();
FGlobalShader::SetParameters<FViewUniformShaderParameters>(RHICmdList, ShaderRHI, View.ViewUniformBuffer);
SetTextureParameter(RHICmdList, ShaderRHI, InTexture, InTextureValue);
OutTexture.SetTexture(RHICmdList, ShaderRHI, 0, OutTextureValue);
SetShaderValue(RHICmdList, ShaderRHI, ViewDimensions, View.ViewRect);
const int32 NumLightsToRenderInSortedLights = TiledDeferredLightsEnd - TiledDeferredLightsStart;
static const auto AllowStaticLightingVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.AllowStaticLighting"));
const bool bAllowStaticLighting = (!AllowStaticLightingVar || AllowStaticLightingVar->GetValueOnRenderThread() != 0);
FTiledDeferredLightData LightData;
FTiledDeferredLightData2 LightData2;
for (int32 LightIndex = 0; LightIndex < NumThisPass; LightIndex++)
{
if (StartIndex + LightIndex < NumLightsToRenderInSortedLights)
{
const FSortedLightSceneInfo& SortedLightInfo = SortedLights[TiledDeferredLightsStart + StartIndex + LightIndex];
const FLightSceneInfo* const LightSceneInfo = SortedLightInfo.LightSceneInfo;
FLightShaderParameters LightParameters;
LightSceneInfo->Proxy->GetLightShaderParameters(LightParameters);
LightData.LightPositionAndInvRadius[LightIndex] = FVector4f(LightParameters.Position, LightParameters.InvRadius);
LightData.LightColorAndFalloffExponent[LightIndex] = FVector4f(LightParameters.Color, LightParameters.FalloffExponent);
if (LightSceneInfo->Proxy->IsInverseSquared())
{
LightData.LightColorAndFalloffExponent[LightIndex].W = 0;
}
// When rendering reflection captures, the direct lighting of the light is actually the indirect specular from the main view
if (View.bIsReflectionCapture)
{
LightData.LightColorAndFalloffExponent[LightIndex].X *= LightSceneInfo->Proxy->GetIndirectLightingScale();
LightData.LightColorAndFalloffExponent[LightIndex].Y *= LightSceneInfo->Proxy->GetIndirectLightingScale();
LightData.LightColorAndFalloffExponent[LightIndex].Z *= LightSceneInfo->Proxy->GetIndirectLightingScale();
}
LightData2.LightDirectionAndSpotlightMaskAndSpecularScale[LightIndex] = FVector4f(LightParameters.Direction, LightParameters.SpecularScale);
// Lights with non-0 length don't support tiled deferred pass, should not have gotten into this list
ensure(LightParameters.SourceLength==0.0f);
LightData2.SpotAnglesAndSourceRadiusAndSimpleLighting[LightIndex] = FVector4f(
LightParameters.SpotAngles.X,
LightParameters.SpotAngles.Y,
LightParameters.SourceRadius,
0.0f);
int32 ShadowMapChannel = LightSceneInfo->Proxy->GetShadowMapChannel();
if (!bAllowStaticLighting)
{
ShadowMapChannel = INDEX_NONE;
}
LightData2.ShadowMapChannelMask[LightIndex] = FVector4f(
ShadowMapChannel == 0 ? 1 : 0,
ShadowMapChannel == 1 ? 1 : 0,
ShadowMapChannel == 2 ? 1 : 0,
ShadowMapChannel == 3 ? 1 : 0);
}
else
{
int32 SimpleLightIndex = StartIndex + LightIndex - NumLightsToRenderInSortedLights;
const FSimpleLightEntry& SimpleLight = SimpleLights.InstanceData[SimpleLightIndex];
const FSimpleLightPerViewEntry& SimpleLightPerViewData = SimpleLights.GetViewDependentData(SimpleLightIndex, ViewIndex, NumViews);
LightData.LightPositionAndInvRadius[LightIndex] = FVector4f(SimpleLightPerViewData.Position, 1.0f / FMath::Max(SimpleLight.Radius, KINDA_SMALL_NUMBER));
LightData.LightColorAndFalloffExponent[LightIndex] = FVector4f(SimpleLight.Color, SimpleLight.Exponent);
LightData2.LightDirectionAndSpotlightMaskAndSpecularScale[LightIndex] = FVector4f(FVector(1, 0, 0), 0);
LightData2.SpotAnglesAndSourceRadiusAndSimpleLighting[LightIndex] = FVector4f(-2, 1, 0, 1.0f);
LightData2.ShadowMapChannelMask[LightIndex] = FVector4f(0, 0, 0, 0);
}
}
SetUniformBufferParameterImmediate(RHICmdList, ShaderRHI, GetUniformBufferParameter<FTiledDeferredLightData>(), LightData);
SetUniformBufferParameterImmediate(RHICmdList, ShaderRHI, GetUniformBufferParameter<FTiledDeferredLightData2>(), LightData2);
SetShaderValue(RHICmdList, ShaderRHI, NumLights, NumThisPass);
}
void UnsetParameters(FRHIComputeCommandList& RHICmdList)
{
OutTexture.UnsetUAV(RHICmdList, RHICmdList.GetBoundComputeShader());
}
static const TCHAR* GetSourceFilename()
{
return TEXT("/Engine/Private/TiledDeferredLightShaders.usf");
}
static const TCHAR* GetFunctionName()
{
return TEXT("TiledDeferredLightingMain");
}
private:
LAYOUT_FIELD(FShaderResourceParameter, InTexture);
LAYOUT_FIELD(FRWShaderParameter, OutTexture);
LAYOUT_FIELD(FShaderParameter, NumLights);
LAYOUT_FIELD(FShaderParameter, ViewDimensions);
};
// #define avoids a lot of code duplication
#define VARIATION1(A) typedef FTiledDeferredLightingCS<A> FTiledDeferredLightingCS##A; \
IMPLEMENT_SHADER_TYPE2(FTiledDeferredLightingCS##A, SF_Compute);
VARIATION1(0) VARIATION1(1)
#undef VARIATION1
bool FDeferredShadingSceneRenderer::CanUseTiledDeferred() const
{
return GUseTiledDeferredShading != 0 && Scene->GetFeatureLevel() >= ERHIFeatureLevel::SM5;
}
bool FDeferredShadingSceneRenderer::ShouldUseTiledDeferred(int32 NumTiledDeferredLights) const
{
// Only use tiled deferred if there are enough unshadowed lights to justify the fixed cost,
return (NumTiledDeferredLights >= GNumLightsBeforeUsingTiledDeferred);
}
template <bool bVisualizeLightCulling>
static void SetShaderTemplTiledLighting(
FRHIComputeCommandList& RHICmdList,
const FViewInfo& View,
int32 ViewIndex,
int32 NumViews,
const TArray<FSortedLightSceneInfo, SceneRenderingAllocator>& SortedLights,
int32 TiledDeferredLightsStart,
int32 TiledDeferredLightsEnd,
const FSimpleLightArray& SimpleLights,
int32 StartIndex,
int32 NumThisPass,
FRHITexture* InTexture,
FRHIUnorderedAccessView* OutTexture)
{
TShaderMapRef<FTiledDeferredLightingCS<bVisualizeLightCulling> > ComputeShader(View.ShaderMap);
RHICmdList.SetComputeShader(ComputeShader.GetComputeShader());
ComputeShader->SetParameters(RHICmdList, View, ViewIndex, NumViews, SortedLights, TiledDeferredLightsStart, TiledDeferredLightsEnd, SimpleLights, StartIndex, NumThisPass, InTexture, OutTexture);
uint32 GroupSizeX = (View.ViewRect.Size().X + GDeferredLightTileSizeX - 1) / GDeferredLightTileSizeX;
uint32 GroupSizeY = (View.ViewRect.Size().Y + GDeferredLightTileSizeY - 1) / GDeferredLightTileSizeY;
DispatchComputeShader(RHICmdList, ComputeShader.GetShader(), GroupSizeX, GroupSizeY, 1);
ComputeShader->UnsetParameters(RHICmdList);
}
BEGIN_SHADER_PARAMETER_STRUCT(FTiledDeferredLightingParameters, )
RDG_TEXTURE_ACCESS(SceneColorInput, ERHIAccess::SRVCompute)
RDG_TEXTURE_ACCESS(SceneColorOutput, ERHIAccess::UAVCompute)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, SceneColorOutputUAV)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneTextureUniformParameters, SceneTextures)
END_SHADER_PARAMETER_STRUCT()
void FDeferredShadingSceneRenderer::RenderTiledDeferredLighting(
FRDGBuilder& GraphBuilder,
FMinimalSceneTextures& SceneTextures,
const TArray<FSortedLightSceneInfo, SceneRenderingAllocator>& SortedLights,
int32 TiledDeferredLightsStart,
int32 TiledDeferredLightsEnd,
const FSimpleLightArray& SimpleLights)
{
const int32 NumUnshadowedLights = TiledDeferredLightsEnd - TiledDeferredLightsStart;
check(GUseTiledDeferredShading);
check(SortedLights.Num() >= NumUnshadowedLights);
const int32 NumLightsToRender = NumUnshadowedLights + SimpleLights.InstanceData.Num();
const int32 NumLightsToRenderInSortedLights = NumUnshadowedLights;
if (NumLightsToRender > 0)
{
INC_DWORD_STAT_BY(STAT_NumLightsUsingTiledDeferred, NumLightsToRender);
INC_DWORD_STAT_BY(STAT_NumLightsUsingSimpleTiledDeferred, SimpleLights.InstanceData.Num());
SCOPE_CYCLE_COUNTER(STAT_DirectLightRenderingTime);
// Determine how many compute shader passes will be needed to process all the lights
const int32 NumPassesNeeded = FMath::DivideAndRoundUp(NumLightsToRender, GMaxNumTiledDeferredLights);
for (int32 PassIndex = 0; PassIndex < NumPassesNeeded; PassIndex++)
{
const int32 StartIndex = PassIndex * GMaxNumTiledDeferredLights;
const int32 NumThisPass = (PassIndex == NumPassesNeeded - 1) ? NumLightsToRender - StartIndex : GMaxNumTiledDeferredLights;
check(NumThisPass > 0);
// One some hardware we can read and write from the same UAV with a 32 bit format. We don't do that yet.
FRDGTextureRef SceneColorOutputTexture = nullptr;
{
FRDGTextureDesc Desc = SceneTextures.Color.Target->Desc;
Desc.Flags |= TexCreate_UAV;
SceneColorOutputTexture = GraphBuilder.CreateTexture(Desc, TEXT("SceneColorTiled"));
}
FRDGTextureUAVRef SceneColorOutputUAV = GraphBuilder.CreateUAV(SceneColorOutputTexture);
const int32 ViewCount = Views.Num();
for (int32 ViewIndex = 0; ViewIndex < ViewCount; ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
FTiledDeferredLightingParameters* PassParameters = GraphBuilder.AllocParameters<FTiledDeferredLightingParameters>();
PassParameters->SceneColorInput = SceneTextures.Color.Target;
PassParameters->SceneColorOutput = SceneColorOutputTexture;
PassParameters->SceneColorOutputUAV = SceneColorOutputUAV;
PassParameters->SceneTextures = SceneTextures.UniformBuffer;
GraphBuilder.AddPass(
RDG_EVENT_NAME("TiledDeferredLighting"),
PassParameters,
ERDGPassFlags::Compute,
[&View, ViewIndex, ViewCount, &SortedLights, TiledDeferredLightsStart, TiledDeferredLightsEnd, &SimpleLights, StartIndex, NumThisPass, SceneColorOutputTexture, SceneColorTexture = SceneTextures.Color.Target, SceneColorOutputUAV](FRHIComputeCommandList& RHICmdList)
{
if (View.Family->EngineShowFlags.VisualizeLightCulling)
{
SetShaderTemplTiledLighting<1>(RHICmdList, View, ViewIndex, ViewCount, SortedLights, TiledDeferredLightsStart, TiledDeferredLightsEnd, SimpleLights, StartIndex, NumThisPass, SceneColorTexture->GetRHI(), SceneColorOutputUAV->GetRHI());
}
else
{
SetShaderTemplTiledLighting<0>(RHICmdList, View, ViewIndex, ViewCount, SortedLights, TiledDeferredLightsStart, TiledDeferredLightsEnd, SimpleLights, StartIndex, NumThisPass, SceneColorTexture->GetRHI(), SceneColorOutputUAV->GetRHI());
}
#if PLATFORM_REQUIRES_UAV_TO_RTV_TEXTURE_CACHE_FLUSH_WORKAROUND
RHICmdList.RHIFlushTextureCacheBOP(SceneColorOutputTexture->GetRHI());
#endif // #if PLATFORM_REQUIRES_UAV_TO_RTV_TEXTURE_CACHE_FLUSH_WORKAROUND
});
}
SceneTextures.Color = SceneColorOutputTexture;
}
}
}