2014-12-07 19:09:38 -05:00
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
2014-03-14 14:13:41 -04:00
/*=============================================================================
TiledDeferredLightRendering . cpp : Implementation of tiled deferred shading
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
# include "RendererPrivate.h"
# include "ScenePrivate.h"
# include "SceneFilterRendering.h"
# include "UniformBuffer.h"
# include "ShaderParameters.h"
# include "LightRendering.h"
# include "ScreenRendering.h"
2014-08-21 06:03:00 -04:00
# include "SceneUtils.h"
2014-03-14 14:13:41 -04:00
/**
* 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_UNIFORM_BUFFER_STRUCT ( FTiledDeferredLightData , )
DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER_ARRAY ( FVector4 , LightPositionAndInvRadius , [ GMaxNumTiledDeferredLights ] )
DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER_ARRAY ( FVector4 , LightColorAndFalloffExponent , [ GMaxNumTiledDeferredLights ] )
END_UNIFORM_BUFFER_STRUCT ( FTiledDeferredLightData )
IMPLEMENT_UNIFORM_BUFFER_STRUCT ( FTiledDeferredLightData , TEXT ( " TiledDeferred " ) ) ;
/** Second constant buffer of light data for tiled deferred. */
BEGIN_UNIFORM_BUFFER_STRUCT ( FTiledDeferredLightData2 , )
DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER_ARRAY ( FVector4 , LightDirectionAndSpotlightMaskAndMinRoughness , [ GMaxNumTiledDeferredLights ] )
DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER_ARRAY ( FVector4 , SpotAnglesAndSourceRadiusAndSimpleLighting , [ GMaxNumTiledDeferredLights ] )
DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER_ARRAY ( FVector4 , ShadowMapChannelMask , [ GMaxNumTiledDeferredLights ] )
END_UNIFORM_BUFFER_STRUCT ( FTiledDeferredLightData2 )
IMPLEMENT_UNIFORM_BUFFER_STRUCT ( FTiledDeferredLightData2 , TEXT ( " TiledDeferred2 " ) ) ;
/** Compute shader used to implement tiled deferred lighting. */
template < bool bVisualizeLightCulling >
class FTiledDeferredLightingCS : public FGlobalShader
{
DECLARE_SHADER_TYPE ( FTiledDeferredLightingCS , Global )
public :
static bool ShouldCache ( EShaderPlatform Platform )
{
return IsFeatureLevelSupported ( Platform , ERHIFeatureLevel : : SM5 ) ;
}
static void ModifyCompilationEnvironment ( EShaderPlatform Platform , FShaderCompilerEnvironment & OutEnvironment )
{
FGlobalShader : : ModifyCompilationEnvironment ( Platform , 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 )
{
DeferredParameters . Bind ( Initializer . ParameterMap ) ;
2014-05-07 17:31:43 -04:00
InTexture . Bind ( Initializer . ParameterMap , TEXT ( " InTexture " ) ) ;
OutTexture . Bind ( Initializer . ParameterMap , TEXT ( " OutTexture " ) ) ;
2014-03-14 14:13:41 -04:00
NumLights . Bind ( Initializer . ParameterMap , TEXT ( " NumLights " ) ) ;
ViewDimensions . Bind ( Initializer . ParameterMap , TEXT ( " ViewDimensions " ) ) ;
PreIntegratedBRDF . Bind ( Initializer . ParameterMap , TEXT ( " PreIntegratedBRDF " ) ) ;
PreIntegratedBRDFSampler . Bind ( Initializer . ParameterMap , TEXT ( " PreIntegratedBRDFSampler " ) ) ;
}
FTiledDeferredLightingCS ( )
{
}
void SetParameters (
2014-06-10 07:29:49 -04:00
FRHICommandList & RHICmdList ,
2014-03-14 14:13:41 -04:00
const FSceneView & View ,
2014-05-02 05:48:07 -04:00
int32 ViewIndex ,
int32 NumViews ,
2014-03-14 14:13:41 -04:00
const TArray < FSortedLightSceneInfo , SceneRenderingAllocator > & SortedLights ,
int32 NumLightsToRenderInSortedLights ,
2014-05-02 05:48:07 -04:00
const FSimpleLightArray & SimpleLights ,
2014-03-14 14:13:41 -04:00
int32 StartIndex ,
2014-05-07 15:05:54 -04:00
int32 NumThisPass ,
2014-05-07 17:31:43 -04:00
IPooledRenderTarget & InTextureValue ,
IPooledRenderTarget & OutTextureValue )
2014-03-14 14:13:41 -04:00
{
FComputeShaderRHIParamRef ShaderRHI = GetComputeShader ( ) ;
2014-06-05 16:38:54 -04:00
FGlobalShader : : SetParameters ( RHICmdList , ShaderRHI , View ) ;
DeferredParameters . Set ( RHICmdList , ShaderRHI , View ) ;
SetTextureParameter ( RHICmdList , ShaderRHI , InTexture , InTextureValue . GetRenderTargetItem ( ) . ShaderResourceTexture ) ;
OutTexture . SetTexture ( RHICmdList , ShaderRHI , 0 , OutTextureValue . GetRenderTargetItem ( ) . UAV ) ;
SetShaderValue ( RHICmdList , ShaderRHI , ViewDimensions , View . ViewRect ) ;
2014-03-14 14:13:41 -04:00
SetTextureParameter (
2014-06-05 16:38:54 -04:00
RHICmdList ,
2014-03-14 14:13:41 -04:00
ShaderRHI ,
PreIntegratedBRDF ,
PreIntegratedBRDFSampler ,
TStaticSamplerState < SF_Bilinear , AM_Clamp , AM_Clamp , AM_Clamp > : : GetRHI ( ) ,
GEngine - > PreIntegratedSkinBRDFTexture - > Resource - > TextureRHI
) ;
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 [ StartIndex + LightIndex ] ;
const FLightSceneInfoCompact & LightSceneInfoCompact = SortedLightInfo . SceneInfo ;
const FLightSceneInfo * const LightSceneInfo = LightSceneInfoCompact . LightSceneInfo ;
FVector NormalizedLightDirection ;
FVector2D SpotAngles ;
float SourceRadius ;
float SourceLength ;
float MinRoughness ;
// Get the light parameters
LightSceneInfo - > Proxy - > GetParameters (
LightData . LightPositionAndInvRadius [ LightIndex ] ,
LightData . LightColorAndFalloffExponent [ LightIndex ] ,
NormalizedLightDirection ,
SpotAngles ,
SourceRadius ,
SourceLength ,
MinRoughness ) ;
if ( LightSceneInfo - > Proxy - > IsInverseSquared ( ) )
{
2014-06-26 16:40:05 -04:00
// Correction for lumen units
LightData . LightColorAndFalloffExponent [ LightIndex ] . X * = 16.0f ;
LightData . LightColorAndFalloffExponent [ LightIndex ] . Y * = 16.0f ;
LightData . LightColorAndFalloffExponent [ LightIndex ] . Z * = 16.0f ;
2014-03-14 14:13:41 -04:00
LightData . LightColorAndFalloffExponent [ LightIndex ] . W = 0 ;
}
2014-07-28 18:01:27 -04:00
// 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 ( ) ;
}
2014-03-14 14:13:41 -04:00
{
// SpotlightMaskAndMinRoughness, >0:Spotlight, MinRoughness = abs();
float W = FMath : : Max ( 0.0001f , MinRoughness ) * ( ( LightSceneInfo - > Proxy - > GetLightType ( ) = = LightType_Spot ) ? 1 : - 1 ) ;
LightData2 . LightDirectionAndSpotlightMaskAndMinRoughness [ LightIndex ] = FVector4 ( NormalizedLightDirection , W ) ;
}
LightData2 . SpotAnglesAndSourceRadiusAndSimpleLighting [ LightIndex ] = FVector4 ( SpotAngles . X , SpotAngles . Y , SourceRadius , 0 ) ;
int32 ShadowMapChannel = LightSceneInfo - > Proxy - > GetShadowMapChannel ( ) ;
if ( ! bAllowStaticLighting )
{
ShadowMapChannel = INDEX_NONE ;
}
LightData2 . ShadowMapChannelMask [ LightIndex ] = FVector4 (
ShadowMapChannel = = 0 ? 1 : 0 ,
ShadowMapChannel = = 1 ? 1 : 0 ,
ShadowMapChannel = = 2 ? 1 : 0 ,
ShadowMapChannel = = 3 ? 1 : 0 ) ;
}
else
{
2014-05-02 05:48:07 -04:00
int32 SimpleLightIndex = StartIndex + LightIndex - NumLightsToRenderInSortedLights ;
const FSimpleLightEntry & SimpleLight = SimpleLights . InstanceData [ SimpleLightIndex ] ;
const FSimpleLightPerViewEntry & SimpleLightPerViewData = SimpleLights . GetViewDependentData ( SimpleLightIndex , ViewIndex , NumViews ) ;
2014-08-28 16:05:15 -04:00
LightData . LightPositionAndInvRadius [ LightIndex ] = FVector4 ( SimpleLightPerViewData . Position , 1.0f / FMath : : Max ( SimpleLight . Radius , KINDA_SMALL_NUMBER ) ) ;
2014-03-14 14:13:41 -04:00
LightData . LightColorAndFalloffExponent [ LightIndex ] = FVector4 ( SimpleLight . Color , SimpleLight . Exponent ) ;
LightData2 . LightDirectionAndSpotlightMaskAndMinRoughness [ LightIndex ] = FVector4 ( FVector ( 1 , 0 , 0 ) , 0 ) ;
LightData2 . SpotAnglesAndSourceRadiusAndSimpleLighting [ LightIndex ] = FVector4 ( - 2 , 1 , 0 , 1 ) ;
LightData2 . ShadowMapChannelMask [ LightIndex ] = FVector4 ( 0 , 0 , 0 , 0 ) ;
2014-06-26 16:40:05 -04:00
if ( SimpleLight . Exponent = = 0.0f )
{
// Correction for lumen units
LightData . LightColorAndFalloffExponent [ LightIndex ] * = 16.0f ;
}
2014-03-14 14:13:41 -04:00
}
}
2014-06-05 16:38:54 -04:00
SetUniformBufferParameterImmediate ( RHICmdList , ShaderRHI , GetUniformBufferParameter < FTiledDeferredLightData > ( ) , LightData ) ;
SetUniformBufferParameterImmediate ( RHICmdList , ShaderRHI , GetUniformBufferParameter < FTiledDeferredLightData2 > ( ) , LightData2 ) ;
SetShaderValue ( RHICmdList , ShaderRHI , NumLights , NumThisPass ) ;
2014-03-14 14:13:41 -04:00
}
2014-06-10 07:29:49 -04:00
void UnsetParameters ( FRHICommandList & RHICmdList )
2014-03-14 14:13:41 -04:00
{
2014-06-05 16:38:54 -04:00
OutTexture . UnsetUAV ( RHICmdList , GetComputeShader ( ) ) ;
2014-03-14 14:13:41 -04:00
}
2015-04-01 07:20:55 -04:00
virtual bool Serialize ( FArchive & Ar ) override
2014-03-14 14:13:41 -04:00
{
bool bShaderHasOutdatedParameters = FGlobalShader : : Serialize ( Ar ) ;
Ar < < DeferredParameters ;
2014-05-07 17:31:43 -04:00
Ar < < OutTexture ;
Ar < < InTexture ;
2014-03-14 14:13:41 -04:00
Ar < < NumLights ;
Ar < < ViewDimensions ;
Ar < < PreIntegratedBRDF ;
Ar < < PreIntegratedBRDFSampler ;
return bShaderHasOutdatedParameters ;
}
static const TCHAR * GetSourceFilename ( )
{
return TEXT ( " TiledDeferredLightShaders " ) ;
}
static const TCHAR * GetFunctionName ( )
{
return TEXT ( " TiledDeferredLightingMain " ) ;
}
private :
FDeferredPixelShaderParameters DeferredParameters ;
2014-05-07 17:31:43 -04:00
FShaderResourceParameter InTexture ;
FRWShaderParameter OutTexture ;
2014-03-14 14:13:41 -04:00
FShaderParameter NumLights ;
FShaderParameter ViewDimensions ;
FShaderResourceParameter PreIntegratedBRDF ;
FShaderResourceParameter PreIntegratedBRDFSampler ;
} ;
// #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
{
2014-05-08 09:05:50 -04:00
return GUseTiledDeferredShading ! = 0 & & Scene - > GetFeatureLevel ( ) > = ERHIFeatureLevel : : SM5 ;
2014-03-14 14:13:41 -04:00
}
bool FDeferredShadingSceneRenderer : : ShouldUseTiledDeferred ( int32 NumUnshadowedLights , int32 NumSimpleLights ) const
{
// Only use tiled deferred if there are enough unshadowed lights to justify the fixed cost,
// Or if there are any simple lights, because those can only be rendered through tiled deferred
return ( NumUnshadowedLights > = GNumLightsBeforeUsingTiledDeferred | | NumSimpleLights > 0 ) ;
}
template < bool bVisualizeLightCulling >
static void SetShaderTemplTiledLighting (
2014-06-27 11:07:13 -04:00
FRHICommandListImmediate & RHICmdList ,
2014-08-28 06:22:54 -04:00
const FViewInfo & View ,
2014-05-02 05:48:07 -04:00
int32 ViewIndex ,
int32 NumViews ,
2014-06-05 16:38:54 -04:00
const TArray < FSortedLightSceneInfo , SceneRenderingAllocator > & SortedLights ,
int32 NumLightsToRenderInSortedLights ,
2014-05-02 05:48:07 -04:00
const FSimpleLightArray & SimpleLights ,
2014-03-14 14:13:41 -04:00
int32 StartIndex ,
2014-05-07 15:05:54 -04:00
int32 NumThisPass ,
2014-05-07 17:31:43 -04:00
IPooledRenderTarget & InTexture ,
IPooledRenderTarget & OutTexture )
2014-03-14 14:13:41 -04:00
{
2014-08-28 06:22:54 -04:00
TShaderMapRef < FTiledDeferredLightingCS < bVisualizeLightCulling > > ComputeShader ( View . ShaderMap ) ;
2014-06-27 11:07:13 -04:00
RHICmdList . SetComputeShader ( ComputeShader - > GetComputeShader ( ) ) ;
2014-03-14 14:13:41 -04:00
2014-06-05 16:38:54 -04:00
ComputeShader - > SetParameters ( RHICmdList , View , ViewIndex , NumViews , SortedLights , NumLightsToRenderInSortedLights , SimpleLights , StartIndex , NumThisPass , InTexture , OutTexture ) ;
2014-03-14 14:13:41 -04:00
uint32 GroupSizeX = ( View . ViewRect . Size ( ) . X + GDeferredLightTileSizeX - 1 ) / GDeferredLightTileSizeX ;
uint32 GroupSizeY = ( View . ViewRect . Size ( ) . Y + GDeferredLightTileSizeY - 1 ) / GDeferredLightTileSizeY ;
2014-06-05 16:38:54 -04:00
DispatchComputeShader ( RHICmdList , * ComputeShader , GroupSizeX , GroupSizeY , 1 ) ;
2014-03-14 14:13:41 -04:00
2014-06-05 16:38:54 -04:00
ComputeShader - > UnsetParameters ( RHICmdList ) ;
2014-03-14 14:13:41 -04:00
}
2014-06-27 11:07:13 -04:00
void FDeferredShadingSceneRenderer : : RenderTiledDeferredLighting ( FRHICommandListImmediate & RHICmdList , const TArray < FSortedLightSceneInfo , SceneRenderingAllocator > & SortedLights , int32 NumUnshadowedLights , const FSimpleLightArray & SimpleLights )
2014-03-14 14:13:41 -04:00
{
check ( GUseTiledDeferredShading ) ;
check ( SortedLights . Num ( ) > = NumUnshadowedLights ) ;
2014-05-02 05:48:07 -04:00
const int32 NumLightsToRender = NumUnshadowedLights + SimpleLights . InstanceData . Num ( ) ;
2014-03-14 14:13:41 -04:00
const int32 NumLightsToRenderInSortedLights = NumUnshadowedLights ;
if ( NumLightsToRender > 0 )
{
INC_DWORD_STAT_BY ( STAT_NumLightsUsingTiledDeferred , NumLightsToRender ) ;
2014-05-02 05:48:07 -04:00
INC_DWORD_STAT_BY ( STAT_NumLightsUsingSimpleTiledDeferred , SimpleLights . InstanceData . Num ( ) ) ;
2014-03-14 14:13:41 -04:00
SCOPE_CYCLE_COUNTER ( STAT_DirectLightRenderingTime ) ;
2014-06-27 11:07:13 -04:00
SetRenderTarget ( RHICmdList , NULL , NULL ) ;
2014-05-07 17:31:43 -04:00
2014-03-14 14:13:41 -04:00
// 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 ) ;
2014-05-07 17:31:43 -04:00
// One some hardware we can read and write from the same UAV with a 32 bit format. We don't do that yet.
TRefCountPtr < IPooledRenderTarget > OutTexture ;
{
2014-06-27 11:07:13 -04:00
GSceneRenderTargets . ResolveSceneColor ( RHICmdList , FResolveRect ( 0 , 0 , ViewFamily . FamilySizeX , ViewFamily . FamilySizeY ) ) ;
2014-05-07 17:31:43 -04:00
FPooledRenderTargetDesc Desc = GSceneRenderTargets . GetSceneColor ( ) - > GetDesc ( ) ;
Desc . TargetableFlags | = TexCreate_UAV ;
GRenderTargetPool . FindFreeElement ( Desc , OutTexture , TEXT ( " SceneColorTiled " ) ) ;
}
2014-03-14 14:13:41 -04:00
{
2014-10-20 10:43:43 -04:00
SCOPED_DRAW_EVENT ( RHICmdList , TiledDeferredLighting ) ;
2014-03-14 14:13:41 -04:00
2014-05-07 17:31:43 -04:00
IPooledRenderTarget & InTexture = * GSceneRenderTargets . GetSceneColor ( ) ;
2014-03-14 14:13:41 -04:00
for ( int32 ViewIndex = 0 ; ViewIndex < Views . Num ( ) ; ViewIndex + + )
{
const FViewInfo & View = Views [ ViewIndex ] ;
if ( View . Family - > EngineShowFlags . VisualizeLightCulling )
{
2014-06-05 16:38:54 -04:00
SetShaderTemplTiledLighting < 1 > ( RHICmdList , View , ViewIndex , Views . Num ( ) , SortedLights , NumLightsToRenderInSortedLights , SimpleLights , StartIndex , NumThisPass , InTexture , * OutTexture ) ;
2014-03-14 14:13:41 -04:00
}
else
{
2014-06-05 16:38:54 -04:00
SetShaderTemplTiledLighting < 0 > ( RHICmdList , View , ViewIndex , Views . Num ( ) , SortedLights , NumLightsToRenderInSortedLights , SimpleLights , StartIndex , NumThisPass , InTexture , * OutTexture ) ;
2014-03-14 14:13:41 -04:00
}
}
}
2014-05-07 17:31:43 -04:00
// swap with the former SceneColor
GSceneRenderTargets . SetSceneColor ( OutTexture ) ;
2014-03-14 14:13:41 -04:00
}
}
}