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
/*=============================================================================
SceneVisibility . cpp : Scene visibility determination .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
# include "RendererPrivate.h"
2014-05-20 07:27:59 -04:00
# include "Engine.h"
2014-03-14 14:13:41 -04:00
# include "ScenePrivate.h"
# include "FXSystem.h"
# include "../../Engine/Private/SkeletalRenderGPUSkin.h" // GPrevPerBoneMotionBlur
2014-08-21 06:03:00 -04:00
# include "SceneUtils.h"
2014-08-28 18:10:45 -04:00
# include "PostProcessing.h"
2014-03-14 14:13:41 -04:00
/*------------------------------------------------------------------------------
Globals
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static float GWireframeCullThreshold = 5.0f ;
static FAutoConsoleVariableRef CVarWireframeCullThreshold (
TEXT ( " r.WireframeCullThreshold " ) ,
GWireframeCullThreshold ,
TEXT ( " Threshold below which objects in ortho wireframe views will be culled. " ) ,
ECVF_RenderThreadSafe
) ;
float GMinScreenRadiusForLights = 0.03f ;
static FAutoConsoleVariableRef CVarMinScreenRadiusForLights (
TEXT ( " r.MinScreenRadiusForLights " ) ,
GMinScreenRadiusForLights ,
TEXT ( " Threshold below which lights will be culled. " ) ,
ECVF_RenderThreadSafe
) ;
float GMinScreenRadiusForDepthPrepass = 0.03f ;
static FAutoConsoleVariableRef CVarMinScreenRadiusForDepthPrepass (
TEXT ( " r.MinScreenRadiusForDepthPrepass " ) ,
GMinScreenRadiusForDepthPrepass ,
TEXT ( " Threshold below which meshes will be culled from depth only pass. " ) ,
ECVF_RenderThreadSafe
) ;
float GMinScreenRadiusForCSMDepth = 0.01f ;
static FAutoConsoleVariableRef CVarMinScreenRadiusForCSMDepth (
TEXT ( " r.MinScreenRadiusForCSMDepth " ) ,
GMinScreenRadiusForCSMDepth ,
TEXT ( " Threshold below which meshes will be culled from CSM depth pass. " ) ,
ECVF_RenderThreadSafe
) ;
2014-06-25 05:47:33 -04:00
static TAutoConsoleVariable < int32 > CVarTemporalAASamples (
TEXT ( " r.TemporalAASamples " ) ,
8 ,
TEXT ( " Number of jittered positions for temporal AA (4, 8=default, 16, 32, 64). " ) ,
ECVF_RenderThreadSafe ) ;
2014-03-14 14:13:41 -04:00
# if PLATFORM_MAC // @todo: disabled until rendering problems with HZB occlusion in OpenGL are solved
static int32 GHZBOcclusion = 0 ;
# else
2015-01-21 15:28:40 -05:00
static int32 GHZBOcclusion = 0 ;
2014-03-14 14:13:41 -04:00
# endif
static FAutoConsoleVariableRef CVarHZBOcclusion (
TEXT ( " r.HZBOcclusion " ) ,
GHZBOcclusion ,
TEXT ( " Defines which occlusion system is used. \n " )
TEXT ( " 0: Hardware occlusion queries \n " )
2014-09-18 17:49:40 -04:00
TEXT ( " 1: Use HZB occlusion system (default, less GPU and CPU cost, more conservative results) " )
TEXT ( " 2: Force HZB occlusion system (overrides rendering platform preferences) " ) ,
2014-03-14 14:13:41 -04:00
ECVF_RenderThreadSafe
) ;
static int32 GVisualizeOccludedPrimitives = 0 ;
static FAutoConsoleVariableRef CVarVisualizeOccludedPrimitives (
TEXT ( " r.VisualizeOccludedPrimitives " ) ,
GVisualizeOccludedPrimitives ,
TEXT ( " Draw boxes for all occluded primitives " ) ,
ECVF_RenderThreadSafe | ECVF_Cheat
) ;
2015-03-04 09:58:16 -05:00
static int32 GAllowSubPrimitiveQueries = 1 ;
static FAutoConsoleVariableRef CVarAllowSubPrimitiveQueries (
TEXT ( " r.AllowSubPrimitiveQueries " ) ,
GAllowSubPrimitiveQueries ,
TEXT ( " Enables sub primitive queries, currently only used by hierarchical instanced static meshes. 1: Enable, 0 Disabled. When disabled, one query is used for the entire proxy. " ) ,
ECVF_RenderThreadSafe
) ;
2014-03-14 14:13:41 -04:00
static TAutoConsoleVariable < int32 > CVarLightShaftQuality (
TEXT ( " r.LightShaftQuality " ) ,
1 ,
TEXT ( " Defines the light shaft quality. \n " )
TEXT ( " 0: off \n " )
2014-11-20 12:36:32 -05:00
TEXT ( " 1: on (default) " ) ,
2014-03-14 14:13:41 -04:00
ECVF_Scalability | ECVF_RenderThreadSafe ) ;
2014-06-25 05:47:33 -04:00
static TAutoConsoleVariable < float > CVarStaticMeshLODDistanceScale (
TEXT ( " r.StaticMeshLODDistanceScale " ) ,
1.0f ,
TEXT ( " Scale factor for the distance used in computing discrete LOD for static meshes. (0.25-1) " ) ,
ECVF_Scalability | ECVF_RenderThreadSafe ) ;
2014-03-14 14:13:41 -04:00
/** Distance fade cvars */
static int32 GDisableLODFade = false ;
static FAutoConsoleVariableRef CVarDisableLODFade ( TEXT ( " r.DisableLODFade " ) , GDisableLODFade , TEXT ( " Disable fading for distance culling " ) , ECVF_RenderThreadSafe ) ;
static float GFadeTime = 0.25f ;
static FAutoConsoleVariableRef CVarLODFadeTime ( TEXT ( " r.LODFadeTime " ) , GFadeTime , TEXT ( " How long LOD takes to fade (in seconds). " ) , ECVF_RenderThreadSafe ) ;
static float GDistanceFadeMaxTravel = 1000.0f ;
static FAutoConsoleVariableRef CVarDistanceFadeMaxTravel ( TEXT ( " r.DistanceFadeMaxTravel " ) , GDistanceFadeMaxTravel , TEXT ( " Max distance that the player can travel during the fade time. " ) , ECVF_RenderThreadSafe ) ;
/*------------------------------------------------------------------------------
Visibility determination .
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/**
* Update a primitive ' s fading state .
* @ param FadingState - State to update .
* @ param View - The view for which to update .
* @ param bVisible - Whether the primitive should be visible in the view .
*/
static void UpdatePrimitiveFadingState ( FPrimitiveFadingState & FadingState , FViewInfo & View , bool bVisible )
{
if ( FadingState . bValid )
{
if ( FadingState . bIsVisible ! = bVisible )
{
float CurrentRealTime = View . Family - > CurrentRealTime ;
// Need to kick off a fade, so make sure that we have fading state for that
if ( ! IsValidRef ( FadingState . UniformBuffer ) )
{
// Primitive is not currently fading. Start a new fade!
FadingState . EndTime = CurrentRealTime + GFadeTime ;
if ( bVisible )
{
// Fading in
// (Time - StartTime) / FadeTime
FadingState . FadeTimeScaleBias . X = 1.0f / GFadeTime ;
FadingState . FadeTimeScaleBias . Y = - CurrentRealTime / GFadeTime ;
}
else
{
// Fading out
// 1 - (Time - StartTime) / FadeTime
FadingState . FadeTimeScaleBias . X = - 1.0f / GFadeTime ;
FadingState . FadeTimeScaleBias . Y = 1.0f + CurrentRealTime / GFadeTime ;
}
FDistanceCullFadeUniformShaderParameters Uniforms ;
Uniforms . FadeTimeScaleBias = FadingState . FadeTimeScaleBias ;
2014-06-05 16:38:54 -04:00
FadingState . UniformBuffer = FDistanceCullFadeUniformBufferRef : : CreateUniformBufferImmediate ( Uniforms , UniformBuffer_MultiFrame ) ;
2014-03-14 14:13:41 -04:00
}
else
{
// Reverse fading direction but maintain current opacity
// Solve for d: a*x+b = -a*x+d
FadingState . FadeTimeScaleBias . Y = 2.0f * CurrentRealTime * FadingState . FadeTimeScaleBias . X + FadingState . FadeTimeScaleBias . Y ;
FadingState . FadeTimeScaleBias . X = - FadingState . FadeTimeScaleBias . X ;
if ( bVisible )
{
// Fading in
// Solve for x: a*x+b = 1
FadingState . EndTime = ( 1.0f - FadingState . FadeTimeScaleBias . Y ) / FadingState . FadeTimeScaleBias . X ;
}
else
{
// Fading out
// Solve for x: a*x+b = 0
FadingState . EndTime = - FadingState . FadeTimeScaleBias . Y / FadingState . FadeTimeScaleBias . X ;
}
FDistanceCullFadeUniformShaderParameters Uniforms ;
Uniforms . FadeTimeScaleBias = FadingState . FadeTimeScaleBias ;
2014-06-05 16:38:54 -04:00
FadingState . UniformBuffer = FDistanceCullFadeUniformBufferRef : : CreateUniformBufferImmediate ( Uniforms , UniformBuffer_MultiFrame ) ;
2014-03-14 14:13:41 -04:00
}
}
}
2014-12-02 13:48:22 -05:00
FadingState . FrameNumber = View . Family - > FrameNumber ;
2014-03-14 14:13:41 -04:00
FadingState . bIsVisible = bVisible ;
FadingState . bValid = true ;
}
bool FViewInfo : : IsDistanceCulled ( float DistanceSquared , float MinDrawDistance , float InMaxDrawDistance , const FPrimitiveSceneInfo * PrimitiveSceneInfo )
{
float MaxDrawDistanceScale = GetCachedScalabilityCVars ( ) . ViewDistanceScale ;
float FadeRadius = GDisableLODFade ? 0.0f : GDistanceFadeMaxTravel ;
float MaxDrawDistance = InMaxDrawDistance * MaxDrawDistanceScale ;
2014-04-23 16:37:26 -04:00
// If cull distance is disabled, always show (except foliage)
if ( Family - > EngineShowFlags . DistanceCulledPrimitives
& & ! PrimitiveSceneInfo - > Proxy - > IsDetailMesh ( ) )
2014-03-14 14:13:41 -04:00
{
return false ;
}
// The primitive is always culled if it exceeds the max fade distance.
if ( DistanceSquared > FMath : : Square ( MaxDrawDistance + FadeRadius ) | |
DistanceSquared < FMath : : Square ( MinDrawDistance ) )
{
return true ;
}
const bool bDistanceCulled = ( DistanceSquared > FMath : : Square ( MaxDrawDistance ) ) ;
const bool bMayBeFading = ( DistanceSquared > FMath : : Square ( MaxDrawDistance - FadeRadius ) ) ;
bool bStillFading = false ;
if ( ! GDisableLODFade & & bMayBeFading & & State ! = NULL & & ! bDisableDistanceBasedFadeTransitions )
{
// Update distance-based visibility and fading state if it has not already been updated.
int32 PrimitiveIndex = PrimitiveSceneInfo - > GetIndex ( ) ;
FRelativeBitReference PrimitiveBit ( PrimitiveIndex ) ;
if ( PotentiallyFadingPrimitiveMap . AccessCorrespondingBit ( PrimitiveBit ) = = false )
{
FPrimitiveFadingState & FadingState = ( ( FSceneViewState * ) State ) - > PrimitiveFadingStates . FindOrAdd ( PrimitiveSceneInfo - > PrimitiveComponentId ) ;
UpdatePrimitiveFadingState ( FadingState , * this , ! bDistanceCulled ) ;
FUniformBufferRHIParamRef UniformBuffer = FadingState . UniformBuffer ;
bStillFading = ( UniformBuffer ! = NULL ) ;
PrimitiveFadeUniformBuffers [ PrimitiveIndex ] = UniformBuffer ;
PotentiallyFadingPrimitiveMap . AccessCorrespondingBit ( PrimitiveBit ) = true ;
}
}
// If we're still fading then make sure the object is still drawn, even if it's beyond the max draw distance
return ( bDistanceCulled & & ! bStillFading ) ;
}
/**
* Frustum cull primitives in the scene against the view .
*/
2014-10-02 14:53:35 -04:00
template < bool UseCustomCulling >
2014-03-14 14:13:41 -04:00
static int32 FrustumCull ( const FScene * Scene , FViewInfo & View )
{
SCOPE_CYCLE_COUNTER ( STAT_FrustumCull ) ;
int32 NumCulledPrimitives = 0 ;
FVector ViewOriginForDistanceCulling = View . ViewMatrices . ViewOrigin ;
float MaxDrawDistanceScale = GetCachedScalabilityCVars ( ) . ViewDistanceScale ;
float FadeRadius = GDisableLODFade ? 0.0f : GDistanceFadeMaxTravel ;
2014-10-02 14:53:35 -04:00
uint8 CustomVisibilityFlags = EOcclusionFlags : : CanBeOccluded | EOcclusionFlags : : HasPrecomputedVisibility ;
2014-03-14 14:13:41 -04:00
for ( FSceneBitArray : : FIterator BitIt ( View . PrimitiveVisibilityMap ) ; BitIt ; + + BitIt )
{
const FPrimitiveBounds & Bounds = Scene - > PrimitiveBounds [ BitIt . GetIndex ( ) ] ;
float DistanceSquared = ( Bounds . Origin - ViewOriginForDistanceCulling ) . SizeSquared ( ) ;
float MaxDrawDistance = Bounds . MaxDrawDistance * MaxDrawDistanceScale ;
2014-10-02 14:53:35 -04:00
int32 VisibilityId = INDEX_NONE ;
if ( UseCustomCulling & &
( ( Scene - > PrimitiveOcclusionFlags [ BitIt . GetIndex ( ) ] & CustomVisibilityFlags ) = = CustomVisibilityFlags ) )
{
VisibilityId = Scene - > PrimitiveVisibilityIds [ BitIt . GetIndex ( ) ] . ByteIndex ;
}
2014-03-14 14:13:41 -04:00
2014-04-23 16:37:26 -04:00
// If cull distance is disabled, always show (except foliage)
if ( View . Family - > EngineShowFlags . DistanceCulledPrimitives
& & ! Scene - > Primitives [ BitIt . GetIndex ( ) ] - > Proxy - > IsDetailMesh ( ) )
2014-03-14 14:13:41 -04:00
{
MaxDrawDistance = FLT_MAX ;
}
// The primitive is always culled if it exceeds the max fade distance or lay outside the view frustum.
if ( DistanceSquared > FMath : : Square ( MaxDrawDistance + FadeRadius ) | |
DistanceSquared < Bounds . MinDrawDistanceSq | |
2014-10-02 14:53:35 -04:00
( UseCustomCulling & & ! View . CustomVisibilityQuery - > IsVisible ( VisibilityId , FBoxSphereBounds ( Bounds . Origin , Bounds . BoxExtent , Bounds . SphereRadius ) ) ) | |
2014-03-14 14:13:41 -04:00
View . ViewFrustum . IntersectSphere ( Bounds . Origin , Bounds . SphereRadius ) = = false | |
View . ViewFrustum . IntersectBox ( Bounds . Origin , Bounds . BoxExtent ) = = false )
{
STAT ( NumCulledPrimitives + + ) ;
continue ;
}
if ( DistanceSquared > FMath : : Square ( MaxDrawDistance ) )
{
View . PotentiallyFadingPrimitiveMap . AccessCorrespondingBit ( BitIt ) = true ;
}
else
{
// The primitive is visible!
BitIt . GetValue ( ) = true ;
if ( DistanceSquared > FMath : : Square ( MaxDrawDistance - FadeRadius ) )
{
View . PotentiallyFadingPrimitiveMap . AccessCorrespondingBit ( BitIt ) = true ;
}
}
}
return NumCulledPrimitives ;
}
/**
* Updated primitive fading states for the view .
*/
static void UpdatePrimitiveFading ( const FScene * Scene , FViewInfo & View )
{
SCOPE_CYCLE_COUNTER ( STAT_UpdatePrimitiveFading ) ;
FSceneViewState * ViewState = ( FSceneViewState * ) View . State ;
if ( ViewState )
{
uint32 PrevFrameNumber = ViewState - > PrevFrameNumber ;
float CurrentRealTime = View . Family - > CurrentRealTime ;
// First clear any stale fading states.
for ( FPrimitiveFadingStateMap : : TIterator It ( ViewState - > PrimitiveFadingStates ) ; It ; + + It )
{
FPrimitiveFadingState & FadingState = It . Value ( ) ;
if ( FadingState . FrameNumber ! = PrevFrameNumber | |
( IsValidRef ( FadingState . UniformBuffer ) & & CurrentRealTime > = FadingState . EndTime ) )
{
It . RemoveCurrent ( ) ;
}
}
// Should we allow fading transitions at all this frame? For frames where the camera moved
// a large distance or where we haven't rendered a view in awhile, it's best to disable
// fading so users don't see unexpected object transitions.
if ( ! GDisableLODFade & & ! View . bDisableDistanceBasedFadeTransitions )
{
// Do a pass over potentially fading primitives and update their states.
for ( FSceneSetBitIterator BitIt ( View . PotentiallyFadingPrimitiveMap ) ; BitIt ; + + BitIt )
{
bool bVisible = View . PrimitiveVisibilityMap . AccessCorrespondingBit ( BitIt ) ;
FPrimitiveFadingState & FadingState = ViewState - > PrimitiveFadingStates . FindOrAdd ( Scene - > PrimitiveComponentIds [ BitIt . GetIndex ( ) ] ) ;
UpdatePrimitiveFadingState ( FadingState , View , bVisible ) ;
FUniformBufferRHIParamRef UniformBuffer = FadingState . UniformBuffer ;
if ( UniformBuffer & & ! bVisible )
{
// If the primitive is fading out make sure it remains visible.
View . PrimitiveVisibilityMap . AccessCorrespondingBit ( BitIt ) = true ;
}
View . PrimitiveFadeUniformBuffers [ BitIt . GetIndex ( ) ] = UniformBuffer ;
}
}
}
}
/**
* Cull occluded primitives in the view .
*/
2014-06-27 11:07:13 -04:00
static int32 OcclusionCull ( FRHICommandListImmediate & RHICmdList , const FScene * Scene , FViewInfo & View )
2014-03-14 14:13:41 -04:00
{
SCOPE_CYCLE_COUNTER ( STAT_OcclusionCull ) ;
// INITVIEWS_TODO: This could be more efficient if broken up in to separate concerns:
// - What is occluded?
// - For which primitives should we render occlusion queries?
// - Generate occlusion query geometry.
int32 NumOccludedPrimitives = 0 ;
FSceneViewState * ViewState = ( FSceneViewState * ) View . State ;
// Disable HZB on OpenGL platforms to avoid rendering artefacts
2014-09-18 17:49:40 -04:00
// It can be forced on by setting HZBOcclusion to 2
bool bHZBOcclusion = ( ! IsOpenGLPlatform ( GShaderPlatformForFeatureLevel [ Scene - > GetFeatureLevel ( ) ] ) & & GHZBOcclusion ) | | ( GHZBOcclusion = = 2 ) ;
2014-03-14 14:13:41 -04:00
// Use precomputed visibility data if it is available.
if ( View . PrecomputedVisibilityData )
{
QUICK_SCOPE_CYCLE_COUNTER ( STAT_LookupPrecomputedVisibility ) ;
uint8 PrecomputedVisibilityFlags = EOcclusionFlags : : CanBeOccluded | EOcclusionFlags : : HasPrecomputedVisibility ;
for ( FSceneSetBitIterator BitIt ( View . PrimitiveVisibilityMap ) ; BitIt ; + + BitIt )
{
if ( ( Scene - > PrimitiveOcclusionFlags [ BitIt . GetIndex ( ) ] & PrecomputedVisibilityFlags ) = = PrecomputedVisibilityFlags )
{
FPrimitiveVisibilityId VisibilityId = Scene - > PrimitiveVisibilityIds [ BitIt . GetIndex ( ) ] ;
if ( ( View . PrecomputedVisibilityData [ VisibilityId . ByteIndex ] & VisibilityId . BitMask ) = = 0 )
{
View . PrimitiveVisibilityMap . AccessCorrespondingBit ( BitIt ) = false ;
INC_DWORD_STAT_BY ( STAT_StaticallyOccludedPrimitives , 1 ) ;
STAT ( NumOccludedPrimitives + + ) ;
}
}
}
}
float CurrentRealTime = View . Family - > CurrentRealTime ;
if ( ViewState )
{
2014-09-05 15:06:43 -04:00
if ( Scene - > GetFeatureLevel ( ) > = ERHIFeatureLevel : : SM4 )
2014-03-14 14:13:41 -04:00
{
bool bClearQueries = ! View . Family - > EngineShowFlags . HitProxies ;
bool bSubmitQueries = ! View . bDisableQuerySubmissions ;
# if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
bSubmitQueries = bSubmitQueries & & ! ViewState - > HasViewParent ( ) & & ! ViewState - > bIsFrozen ;
# endif
if ( bHZBOcclusion )
{
QUICK_SCOPE_CYCLE_COUNTER ( STAT_MapHZBResults ) ;
2014-10-27 14:02:38 -04:00
check ( ! ViewState - > HZBOcclusionTests . IsValidFrame ( ViewState - > OcclusionFrameCounter ) ) ;
2014-06-27 11:07:13 -04:00
ViewState - > HZBOcclusionTests . MapResults ( RHICmdList ) ;
2014-03-14 14:13:41 -04:00
}
FViewElementPDI OcclusionPDI ( & View , NULL ) ;
QUICK_SCOPE_CYCLE_COUNTER ( STAT_FetchVisibilityForPrimitives ) ;
for ( FSceneSetBitIterator BitIt ( View . PrimitiveVisibilityMap ) ; BitIt ; + + BitIt )
{
uint8 OcclusionFlags = Scene - > PrimitiveOcclusionFlags [ BitIt . GetIndex ( ) ] ;
bool bCanBeOccluded = ( OcclusionFlags & EOcclusionFlags : : CanBeOccluded ) ! = 0 ;
if ( GIsEditor )
{
FPrimitiveSceneInfo * PrimitiveSceneInfo = Scene - > Primitives [ BitIt . GetIndex ( ) ] ;
if ( PrimitiveSceneInfo - > Proxy - > IsSelected ( ) )
{
// to render occluded outline for selected objects
bCanBeOccluded = false ;
}
}
2015-03-04 09:58:16 -05:00
int32 NumSubQueries = 1 ;
bool bSubQueries = false ;
const TArray < FBoxSphereBounds > * SubBounds = nullptr ;
TArray < bool , SceneRenderingAllocator > SubIsOccluded ;
2014-03-14 14:13:41 -04:00
2015-03-04 09:58:16 -05:00
if ( ( OcclusionFlags & EOcclusionFlags : : HasSubprimitiveQueries ) & & GAllowSubPrimitiveQueries )
2014-03-14 14:13:41 -04:00
{
2015-03-04 09:58:16 -05:00
FPrimitiveSceneProxy * Proxy = Scene - > Primitives [ BitIt . GetIndex ( ) ] - > Proxy ;
SubBounds = Proxy - > GetOcclusionQueries ( & View ) ;
NumSubQueries = SubBounds - > Num ( ) ;
bSubQueries = true ;
if ( ! NumSubQueries )
2014-03-14 14:13:41 -04:00
{
2015-03-04 09:58:16 -05:00
View . PrimitiveVisibilityMap . AccessCorrespondingBit ( BitIt ) = false ;
continue ;
2014-03-14 14:13:41 -04:00
}
2015-03-04 09:58:16 -05:00
SubIsOccluded . Reserve ( NumSubQueries ) ;
}
2014-03-14 14:13:41 -04:00
2015-03-04 09:58:16 -05:00
bool bAllSubOcclusionStateIsDefinite = true ;
bool bAllSubOccluded = true ;
FPrimitiveComponentId PrimitiveId = Scene - > PrimitiveComponentIds [ BitIt . GetIndex ( ) ] ;
for ( int32 SubQuery = 0 ; SubQuery < NumSubQueries ; SubQuery + + )
{
FPrimitiveOcclusionHistory * PrimitiveOcclusionHistory = ViewState - > PrimitiveOcclusionHistorySet . Find ( FPrimitiveOcclusionHistoryKey ( PrimitiveId , SubQuery ) ) ;
bool bIsOccluded = false ;
bool bOcclusionStateIsDefinite = false ;
if ( ! PrimitiveOcclusionHistory )
{
// If the primitive doesn't have an occlusion history yet, create it.
PrimitiveOcclusionHistory = & ViewState - > PrimitiveOcclusionHistorySet [
ViewState - > PrimitiveOcclusionHistorySet . Add ( FPrimitiveOcclusionHistory ( PrimitiveId , SubQuery ) )
] ;
// If the primitive hasn't been visible recently enough to have a history, treat it as unoccluded this frame so it will be rendered as an occluder and its true occlusion state can be determined.
// already set bIsOccluded = false;
// Flag the primitive's occlusion state as indefinite, which will force it to be queried this frame.
// The exception is if the primitive isn't occludable, in which case we know that it's definitely unoccluded.
bOcclusionStateIsDefinite = bCanBeOccluded ? false : true ;
}
else
{
if ( View . bIgnoreExistingQueries )
{
// If the view is ignoring occlusion queries, the primitive is definitely unoccluded.
// already set bIsOccluded = false;
bOcclusionStateIsDefinite = View . bDisableQuerySubmissions ;
}
else if ( bCanBeOccluded )
{
if ( bHZBOcclusion )
{
if ( ViewState - > HZBOcclusionTests . IsValidFrame ( PrimitiveOcclusionHistory - > HZBTestFrameNumber ) )
{
bIsOccluded = ! ViewState - > HZBOcclusionTests . IsVisible ( PrimitiveOcclusionHistory - > HZBTestIndex ) ;
bOcclusionStateIsDefinite = true ;
}
}
else
{
// Read the occlusion query results.
uint64 NumSamples = 0 ;
FRenderQueryRHIRef & PastQuery = PrimitiveOcclusionHistory - > GetPastQuery ( ViewState - > OcclusionFrameCounter ) ;
if ( IsValidRef ( PastQuery ) )
{
// NOTE: RHIGetOcclusionQueryResult should never fail when using a blocking call, rendering artifacts may show up.
if ( RHICmdList . GetRenderQueryResult ( PastQuery , NumSamples , true ) )
2014-03-14 14:13:41 -04:00
{
2015-03-04 09:58:16 -05:00
// we render occlusion without MSAA
uint32 NumPixels = ( uint32 ) NumSamples ;
// The primitive is occluded if none of its bounding box's pixels were visible in the previous frame's occlusion query.
bIsOccluded = ( NumPixels = = 0 ) ;
if ( ! bIsOccluded )
{
checkSlow ( View . OneOverNumPossiblePixels > 0.0f ) ;
PrimitiveOcclusionHistory - > LastPixelsPercentage = NumPixels * View . OneOverNumPossiblePixels ;
}
else
{
PrimitiveOcclusionHistory - > LastPixelsPercentage = 0.0f ;
}
// Flag the primitive's occlusion state as definite if it wasn't grouped.
bOcclusionStateIsDefinite = ! PrimitiveOcclusionHistory - > bGroupedQuery ;
2014-03-14 14:13:41 -04:00
}
else
2015-03-04 09:58:16 -05:00
{
// If the occlusion query failed, treat the primitive as visible.
// already set bIsOccluded = false;
}
}
else
{
// If there's no occlusion query for the primitive, set it's visibility state to whether it has been unoccluded recently.
bIsOccluded = ( PrimitiveOcclusionHistory - > LastVisibleTime + GEngine - > PrimitiveProbablyVisibleTime < CurrentRealTime ) ;
if ( bIsOccluded )
2014-03-14 14:13:41 -04:00
{
PrimitiveOcclusionHistory - > LastPixelsPercentage = 0.0f ;
}
2015-03-04 09:58:16 -05:00
else
{
PrimitiveOcclusionHistory - > LastPixelsPercentage = GEngine - > MaxOcclusionPixelsFraction ;
}
2014-03-14 14:13:41 -04:00
2015-03-04 09:58:16 -05:00
// the state was definite last frame, otherwise we would have ran a query
bOcclusionStateIsDefinite = true ;
2014-03-14 14:13:41 -04:00
}
}
2015-03-04 09:58:16 -05:00
if ( GVisualizeOccludedPrimitives & & bIsOccluded )
2014-03-14 14:13:41 -04:00
{
2015-03-04 09:58:16 -05:00
const FBoxSphereBounds & Bounds = bSubQueries ? ( * SubBounds ) [ SubQuery ] : Scene - > PrimitiveOcclusionBounds [ BitIt . GetIndex ( ) ] ;
DrawWireBox ( & OcclusionPDI , Bounds . GetBox ( ) , FColor ( 50 , 255 , 50 ) , SDPG_Foreground ) ;
2014-03-14 14:13:41 -04:00
}
}
else
{
2015-03-04 09:58:16 -05:00
// Primitives that aren't occludable are considered definitely unoccluded.
// already set bIsOccluded = false;
bOcclusionStateIsDefinite = true ;
}
2014-03-14 14:13:41 -04:00
2015-03-04 09:58:16 -05:00
if ( bClearQueries )
{
ViewState - > OcclusionQueryPool . ReleaseQuery ( RHICmdList , PrimitiveOcclusionHistory - > GetPastQuery ( ViewState - > OcclusionFrameCounter ) ) ;
}
}
// Set the primitive's considered time to keep its occlusion history from being trimmed.
PrimitiveOcclusionHistory - > LastConsideredTime = CurrentRealTime ;
if ( bSubmitQueries & & bCanBeOccluded )
{
bool bAllowBoundsTest ;
const FBoxSphereBounds & OcclusionBounds = bSubQueries ? ( * SubBounds ) [ SubQuery ] : Scene - > PrimitiveOcclusionBounds [ BitIt . GetIndex ( ) ] ;
if ( View . bHasNearClippingPlane )
{
bAllowBoundsTest = View . NearClippingPlane . PlaneDot ( OcclusionBounds . Origin ) <
- ( FVector : : BoxPushOut ( View . NearClippingPlane , OcclusionBounds . BoxExtent ) ) ;
}
2015-03-31 14:03:15 -04:00
else if ( ! View . IsPerspectiveProjection ( ) )
{
// Transform parallel near plane
2015-04-01 10:53:07 -04:00
static_assert ( ( int32 ) ERHIZBuffer : : IsInverted ! = 0 , " Check equation for culling! " ) ;
2015-03-31 14:03:15 -04:00
bAllowBoundsTest = View . WorldToScreen ( OcclusionBounds . Origin ) . Z - View . ViewMatrices . ProjMatrix . M [ 2 ] [ 2 ] * OcclusionBounds . SphereRadius < 1 ;
}
2015-03-04 09:58:16 -05:00
else
{
bAllowBoundsTest = OcclusionBounds . SphereRadius < HALF_WORLD_MAX ;
}
if ( bAllowBoundsTest )
{
if ( bHZBOcclusion )
2014-03-14 14:13:41 -04:00
{
2015-03-04 09:58:16 -05:00
// Always run
PrimitiveOcclusionHistory - > HZBTestIndex = ViewState - > HZBOcclusionTests . AddBounds ( OcclusionBounds . Origin , OcclusionBounds . BoxExtent ) ;
PrimitiveOcclusionHistory - > HZBTestFrameNumber = ViewState - > OcclusionFrameCounter ;
2014-03-14 14:13:41 -04:00
}
else
{
2015-03-04 09:58:16 -05:00
// decide if a query should be run this frame
bool bRunQuery , bGroupedQuery ;
2014-03-14 14:13:41 -04:00
2015-03-04 09:58:16 -05:00
if ( ! bSubQueries & & // sub queries are never grouped, we assume the custom code knows what it is doing and will group internally if it wants
( OcclusionFlags & EOcclusionFlags : : AllowApproximateOcclusion ) )
{
if ( bIsOccluded )
{
// Primitives that were occluded the previous frame use grouped queries.
bGroupedQuery = true ;
bRunQuery = true ;
}
else if ( bOcclusionStateIsDefinite )
{
// If the primitive's is definitely unoccluded, only requery it occasionally.
float FractionMultiplier = FMath : : Max ( PrimitiveOcclusionHistory - > LastPixelsPercentage / GEngine - > MaxOcclusionPixelsFraction , 1.0f ) ;
bRunQuery = ( FractionMultiplier * GOcclusionRandomStream . GetFraction ( ) < GEngine - > MaxOcclusionPixelsFraction ) ;
bGroupedQuery = false ;
}
else
{
bGroupedQuery = false ;
bRunQuery = true ;
}
}
else
{
// Primitives that need precise occlusion results use individual queries.
bGroupedQuery = false ;
bRunQuery = true ;
}
if ( bRunQuery )
{
PrimitiveOcclusionHistory - > SetCurrentQuery ( ViewState - > OcclusionFrameCounter ,
bGroupedQuery ?
View . GroupedOcclusionQueries . BatchPrimitive ( OcclusionBounds . Origin + View . ViewMatrices . PreViewTranslation , OcclusionBounds . BoxExtent ) :
View . IndividualOcclusionQueries . BatchPrimitive ( OcclusionBounds . Origin + View . ViewMatrices . PreViewTranslation , OcclusionBounds . BoxExtent )
) ;
}
PrimitiveOcclusionHistory - > bGroupedQuery = bGroupedQuery ;
2014-03-14 14:13:41 -04:00
}
2015-03-04 09:58:16 -05:00
}
else
{
// If the primitive's bounding box intersects the near clipping plane, treat it as definitely unoccluded.
bIsOccluded = false ;
bOcclusionStateIsDefinite = true ;
}
}
if ( bSubQueries )
{
SubIsOccluded . Add ( bIsOccluded ) ;
if ( ! bIsOccluded )
{
bAllSubOccluded = false ;
if ( bOcclusionStateIsDefinite )
{
PrimitiveOcclusionHistory - > LastVisibleTime = CurrentRealTime ;
}
}
if ( bIsOccluded | | ! bOcclusionStateIsDefinite )
{
bAllSubOcclusionStateIsDefinite = false ;
2014-03-14 14:13:41 -04:00
}
}
else
{
2015-03-04 09:58:16 -05:00
if ( bIsOccluded )
{
View . PrimitiveVisibilityMap . AccessCorrespondingBit ( BitIt ) = false ;
STAT ( NumOccludedPrimitives + + ) ;
}
else if ( bOcclusionStateIsDefinite )
{
PrimitiveOcclusionHistory - > LastVisibleTime = CurrentRealTime ;
View . PrimitiveDefinitelyUnoccludedMap . AccessCorrespondingBit ( BitIt ) = true ;
}
2014-03-14 14:13:41 -04:00
}
}
2015-03-04 09:58:16 -05:00
if ( bSubQueries )
2014-03-14 14:13:41 -04:00
{
2015-03-04 09:58:16 -05:00
FPrimitiveSceneProxy * Proxy = Scene - > Primitives [ BitIt . GetIndex ( ) ] - > Proxy ;
Proxy - > AcceptOcclusionResults ( & View , & SubIsOccluded [ 0 ] , SubIsOccluded . Num ( ) ) ;
if ( bAllSubOccluded )
{
View . PrimitiveVisibilityMap . AccessCorrespondingBit ( BitIt ) = false ;
STAT ( NumOccludedPrimitives + + ) ;
}
else if ( bAllSubOcclusionStateIsDefinite )
{
View . PrimitiveDefinitelyUnoccludedMap . AccessCorrespondingBit ( BitIt ) = true ;
}
2014-03-14 14:13:41 -04:00
}
}
if ( bHZBOcclusion )
{
QUICK_SCOPE_CYCLE_COUNTER ( STAT_HZBUnmapResults ) ;
2014-06-27 11:07:13 -04:00
ViewState - > HZBOcclusionTests . UnmapResults ( RHICmdList ) ;
2014-03-14 14:13:41 -04:00
if ( bSubmitQueries )
{
2014-10-27 14:02:38 -04:00
ViewState - > HZBOcclusionTests . SetValidFrameNumber ( ViewState - > OcclusionFrameCounter ) ;
2014-03-14 14:13:41 -04:00
}
}
}
else
{
// No occlusion queries, so mark primitives as not occluded
for ( FSceneSetBitIterator BitIt ( View . PrimitiveVisibilityMap ) ; BitIt ; + + BitIt )
{
View . PrimitiveDefinitelyUnoccludedMap . AccessCorrespondingBit ( BitIt ) = true ;
}
}
}
return NumOccludedPrimitives ;
}
2014-09-15 13:41:48 -04:00
template < class T >
struct FRelevancePrimSet
{
enum
{
MaxPrims = 127 //like 128, but we leave space for NumPrims
} ;
int32 NumPrims ;
T Prims [ MaxPrims ] ;
FORCEINLINE FRelevancePrimSet ( )
: NumPrims ( 0 )
{
//FMemory::Memzero(Prims, sizeof(T) * MaxPrims);
}
FORCEINLINE void AddPrim ( T Prim )
{
checkSlow ( NumPrims < MaxPrims ) ;
Prims [ NumPrims + + ] = Prim ;
}
FORCEINLINE bool IsFull ( ) const
{
return NumPrims > = MaxPrims ;
}
template < class TARRAY >
FORCEINLINE void AppendTo ( TARRAY & Array )
{
Array . Append ( Prims , NumPrims ) ;
}
} ;
struct FMarkRelevantStaticMeshesForViewData
{
FVector ViewOrigin ;
float MaxDrawDistanceScaleSquared ;
int32 ForcedLODLevel ;
float LODScale ;
float InvLODScale ;
float MinScreenRadiusForCSMDepthSquared ;
float MinScreenRadiusForDepthPrepassSquared ;
bool bForceEarlyZPass ;
FMarkRelevantStaticMeshesForViewData ( FViewInfo & View )
{
ViewOrigin = View . ViewMatrices . ViewOrigin ;
MaxDrawDistanceScaleSquared = GetCachedScalabilityCVars ( ) . ViewDistanceScaleSquared ;
// outside of the loop to be more efficient
ForcedLODLevel = ( View . Family - > EngineShowFlags . LOD ) ? GetCVarForceLOD ( ) : 0 ;
LODScale = CVarStaticMeshLODDistanceScale . GetValueOnRenderThread ( ) ;
InvLODScale = 1.0f / LODScale ;
MinScreenRadiusForCSMDepthSquared = GMinScreenRadiusForCSMDepth * GMinScreenRadiusForCSMDepth ;
MinScreenRadiusForDepthPrepassSquared = GMinScreenRadiusForDepthPrepass * GMinScreenRadiusForDepthPrepass ;
extern TAutoConsoleVariable < int32 > CVarEarlyZPass ;
bForceEarlyZPass = CVarEarlyZPass . GetValueOnRenderThread ( ) = = 2 ;
}
} ;
namespace EMarkMaskBits
{
enum Type
{
StaticMeshShadowDepthMapMask = 0x1 ,
StaticMeshVisibilityMapMask = 0x2 ,
StaticMeshVelocityMapMask = 0x4 ,
StaticMeshOccluderMapMask = 0x8 ,
2015-04-24 11:20:23 -04:00
StaticMeshFadeOutDitheredLODMapMask = 0x10 ,
StaticMeshFadeInDitheredLODMapMask = 0x20 ,
2014-09-15 13:41:48 -04:00
} ;
}
struct FRelevancePacket
{
const float CurrentWorldTime ;
const float DeltaWorldTime ;
FRHICommandListImmediate & RHICmdList ;
const FScene * Scene ;
const FViewInfo & View ;
const uint8 ViewBit ;
const FMarkRelevantStaticMeshesForViewData & ViewData ;
FPrimitiveViewMasks & OutHasDynamicMeshElementsMasks ;
FPrimitiveViewMasks & OutHasDynamicEditorMeshElementsMasks ;
uint8 * RESTRICT MarkMasks ;
FRelevancePrimSet < int32 > Input ;
FRelevancePrimSet < int32 > RelevantStaticPrimitives ;
FRelevancePrimSet < int32 > NotDrawRelevant ;
FRelevancePrimSet < FPrimitiveSceneInfo * > VisibleDynamicPrimitives ;
FRelevancePrimSet < FTranslucentPrimSet : : FSortedPrim > SortedSeparateTranslucencyPrims ;
FRelevancePrimSet < FTranslucentPrimSet : : FSortedPrim > SortedTranslucencyPrims ;
FRelevancePrimSet < FPrimitiveSceneProxy * > DistortionPrimSet ;
FRelevancePrimSet < FPrimitiveSceneProxy * > CustomDepthSet ;
FRelevancePrimSet < FPrimitiveSceneInfo * > UpdateStaticMeshes ;
FRelevancePrimSet < FPrimitiveSceneInfo * > VisibleEditorPrimitives ;
2015-04-13 17:01:36 -04:00
uint16 CombinedShadingModelMask ;
2014-09-15 13:41:48 -04:00
FRelevancePacket (
FRHICommandListImmediate & InRHICmdList ,
const FScene * InScene ,
const FViewInfo & InView ,
uint8 InViewBit ,
const FMarkRelevantStaticMeshesForViewData & InViewData ,
FPrimitiveViewMasks & InOutHasDynamicMeshElementsMasks ,
FPrimitiveViewMasks & InOutHasDynamicEditorMeshElementsMasks ,
uint8 * InMarkMasks )
: CurrentWorldTime ( InView . Family - > CurrentWorldTime )
, DeltaWorldTime ( InView . Family - > DeltaWorldTime )
, RHICmdList ( InRHICmdList )
, Scene ( InScene )
, View ( InView )
, ViewBit ( InViewBit )
, ViewData ( InViewData )
, OutHasDynamicMeshElementsMasks ( InOutHasDynamicMeshElementsMasks )
, OutHasDynamicEditorMeshElementsMasks ( InOutHasDynamicEditorMeshElementsMasks )
, MarkMasks ( InMarkMasks )
2015-04-13 17:01:36 -04:00
, CombinedShadingModelMask ( 0 )
2014-09-15 13:41:48 -04:00
{
}
void AnyThreadTask ( )
{
ComputeRelevance ( ) ;
MarkRelevant ( ) ;
}
void ComputeRelevance ( )
{
2015-04-13 17:01:36 -04:00
CombinedShadingModelMask = 0 ;
2014-09-15 13:41:48 -04:00
SCOPE_CYCLE_COUNTER ( STAT_ComputeViewRelevance ) ;
for ( int32 Index = 0 ; Index < Input . NumPrims ; Index + + )
{
int32 BitIndex = Input . Prims [ Index ] ;
FPrimitiveSceneInfo * PrimitiveSceneInfo = Scene - > Primitives [ BitIndex ] ;
FPrimitiveViewRelevance & ViewRelevance = const_cast < FPrimitiveViewRelevance & > ( View . PrimitiveViewRelevanceMap [ BitIndex ] ) ;
ViewRelevance = PrimitiveSceneInfo - > Proxy - > GetViewRelevance ( & View ) ;
ViewRelevance . bInitializedThisFrame = true ;
const bool bStaticRelevance = ViewRelevance . bStaticRelevance ;
const bool bDrawRelevance = ViewRelevance . bDrawRelevance ;
const bool bDynamicRelevance = ViewRelevance . bDynamicRelevance ;
const bool bShadowRelevance = ViewRelevance . bShadowRelevance ;
const bool bEditorRelevance = ViewRelevance . bEditorPrimitiveRelevance ;
const bool bTranslucentRelevance = ViewRelevance . HasTranslucency ( ) ;
if ( bStaticRelevance & & ( bDrawRelevance | | bShadowRelevance ) )
{
RelevantStaticPrimitives . AddPrim ( BitIndex ) ;
}
if ( ! bDrawRelevance )
{
NotDrawRelevant . AddPrim ( BitIndex ) ;
continue ;
}
if ( bEditorRelevance )
{
// Editor primitives are rendered after post processing and composited onto the scene
VisibleEditorPrimitives . AddPrim ( PrimitiveSceneInfo ) ;
if ( GIsEditor )
{
OutHasDynamicEditorMeshElementsMasks [ BitIndex ] | = ViewBit ;
}
}
else if ( bDynamicRelevance )
{
// Keep track of visible dynamic primitives.
VisibleDynamicPrimitives . AddPrim ( PrimitiveSceneInfo ) ;
OutHasDynamicMeshElementsMasks [ BitIndex ] | = ViewBit ;
}
if ( ViewRelevance . HasTranslucency ( ) & & ! bEditorRelevance & & ViewRelevance . bRenderInMainPass )
{
// Add to set of dynamic translucent primitives
FTranslucentPrimSet : : PlaceScenePrimitive ( PrimitiveSceneInfo , View , ViewRelevance . bNormalTranslucencyRelevance , ViewRelevance . bSeparateTranslucencyRelevance ,
& SortedTranslucencyPrims . Prims [ SortedTranslucencyPrims . NumPrims ] , SortedTranslucencyPrims . NumPrims ,
& SortedSeparateTranslucencyPrims . Prims [ SortedSeparateTranslucencyPrims . NumPrims ] , SortedSeparateTranslucencyPrims . NumPrims
) ;
if ( ViewRelevance . bDistortionRelevance )
{
// Add to set of dynamic distortion primitives
DistortionPrimSet . AddPrim ( PrimitiveSceneInfo - > Proxy ) ;
}
}
2015-04-13 17:01:36 -04:00
CombinedShadingModelMask | = ViewRelevance . ShadingModelMaskRelevance ;
2014-09-15 13:41:48 -04:00
if ( ViewRelevance . bRenderCustomDepth )
{
// Add to set of dynamic distortion primitives
CustomDepthSet . AddPrim ( PrimitiveSceneInfo - > Proxy ) ;
}
// INITVIEWS_TODO: Do this in a separate pass? There are no dependencies
// here except maybe ParentPrimitives. This could be done in a
// low-priority background task and forgotten about.
// If the primitive's last render time is older than last frame, consider
// it newly visible and update its visibility change time
if ( PrimitiveSceneInfo - > LastRenderTime < CurrentWorldTime - DeltaWorldTime - DELTA )
{
PrimitiveSceneInfo - > LastVisibilityChangeTime = CurrentWorldTime ;
}
PrimitiveSceneInfo - > LastRenderTime = CurrentWorldTime ;
// If the primitive is definitely unoccluded or if in Wireframe mode and the primitive is estimated
// to be unoccluded, then update the primitive components's LastRenderTime
// on the game thread. This signals that the primitive is visible.
if ( View . PrimitiveDefinitelyUnoccludedMap [ BitIndex ] | | ( View . Family - > EngineShowFlags . Wireframe & & View . PrimitiveVisibilityMap [ BitIndex ] ) )
{
// Update the PrimitiveComponent's LastRenderTime.
* ( PrimitiveSceneInfo - > ComponentLastRenderTime ) = CurrentWorldTime ;
}
// Cache the nearest reflection proxy if needed
if ( PrimitiveSceneInfo - > bNeedsCachedReflectionCaptureUpdate
2014-10-09 17:50:34 -04:00
// During Forward Shading, the per-object reflection is used for everything
// Otherwise it is just used on translucency
& & ( ! Scene - > ShouldUseDeferredRenderer ( ) | | bTranslucentRelevance ) )
2014-09-15 13:41:48 -04:00
{
PrimitiveSceneInfo - > CachedReflectionCaptureProxy = Scene - > FindClosestReflectionCapture ( Scene - > PrimitiveBounds [ BitIndex ] . Origin ) ;
PrimitiveSceneInfo - > bNeedsCachedReflectionCaptureUpdate = false ;
}
if ( PrimitiveSceneInfo - > NeedsUpdateStaticMeshes ( ) )
{
UpdateStaticMeshes . AddPrim ( PrimitiveSceneInfo ) ;
}
}
}
void MarkRelevant ( )
{
SCOPE_CYCLE_COUNTER ( STAT_StaticRelevance ) ;
// using a local counter to reduce memory traffic
int32 NumVisibleStaticMeshElements = 0 ;
FViewInfo & WriteView = const_cast < FViewInfo & > ( View ) ;
for ( int32 StaticPrimIndex = 0 , Num = RelevantStaticPrimitives . NumPrims ; StaticPrimIndex < Num ; + + StaticPrimIndex )
{
int32 PrimitiveIndex = RelevantStaticPrimitives . Prims [ StaticPrimIndex ] ;
2014-10-01 14:45:04 -04:00
const FPrimitiveSceneInfo * RESTRICT PrimitiveSceneInfo = Scene - > Primitives [ PrimitiveIndex ] ;
const FPrimitiveBounds & Bounds = Scene - > PrimitiveBounds [ PrimitiveIndex ] ;
const FPrimitiveViewRelevance & ViewRelevance = View . PrimitiveViewRelevanceMap [ PrimitiveIndex ] ;
2014-09-15 13:41:48 -04:00
2015-04-24 11:20:23 -04:00
FLODMask LODToRender = ComputeLODForMeshes ( PrimitiveSceneInfo - > StaticMeshes , View , Bounds . Origin , Bounds . SphereRadius , ViewData . ForcedLODLevel , ViewData . LODScale ) ;
2014-09-15 13:41:48 -04:00
float DistanceSquared = ( Bounds . Origin - ViewData . ViewOrigin ) . SizeSquared ( ) ;
const float LODFactorDistanceSquared = DistanceSquared * FMath : : Square ( View . LODDistanceFactor * ViewData . InvLODScale ) ;
const bool bDrawShadowDepth = FMath : : Square ( Bounds . SphereRadius ) > ViewData . MinScreenRadiusForCSMDepthSquared * LODFactorDistanceSquared ;
const bool bDrawDepthOnly = ViewData . bForceEarlyZPass | | FMath : : Square ( Bounds . SphereRadius ) > GMinScreenRadiusForDepthPrepass * GMinScreenRadiusForDepthPrepass * LODFactorDistanceSquared ;
const int32 NumStaticMeshes = PrimitiveSceneInfo - > StaticMeshes . Num ( ) ;
for ( int32 MeshIndex = 0 ; MeshIndex < NumStaticMeshes ; MeshIndex + + )
{
const FStaticMesh & StaticMesh = PrimitiveSceneInfo - > StaticMeshes [ MeshIndex ] ;
2015-04-24 11:20:23 -04:00
if ( LODToRender . ContainsLOD ( StaticMesh . LODIndex ) )
2014-09-15 13:41:48 -04:00
{
uint8 MarkMask = 0 ;
bool bNeedsBatchVisibility = false ;
2015-04-24 11:20:23 -04:00
if ( LODToRender . IsDithered ( ) )
{
if ( LODToRender . DitheredLODIndices [ 0 ] = = StaticMesh . LODIndex )
{
MarkMask | = EMarkMaskBits : : StaticMeshFadeOutDitheredLODMapMask ;
}
if ( LODToRender . DitheredLODIndices [ 1 ] = = StaticMesh . LODIndex )
{
MarkMask | = EMarkMaskBits : : StaticMeshFadeInDitheredLODMapMask ;
}
}
2014-09-15 13:41:48 -04:00
if ( ViewRelevance . bShadowRelevance & & bDrawShadowDepth & & StaticMesh . CastShadow )
{
// Mark static mesh as visible in shadows.
MarkMask | = EMarkMaskBits : : StaticMeshShadowDepthMapMask ;
bNeedsBatchVisibility = true ;
}
if ( ViewRelevance . bDrawRelevance & & ! StaticMesh . bShadowOnly & & ( ViewRelevance . bRenderInMainPass | | ViewRelevance . bRenderCustomDepth ) )
{
// Mark static mesh as visible for rendering
MarkMask | = EMarkMaskBits : : StaticMeshVisibilityMapMask ;
if ( PrimitiveSceneInfo - > ShouldRenderVelocity ( View , false ) )
{
MarkMask | = EMarkMaskBits : : StaticMeshVelocityMapMask ;
}
+ + NumVisibleStaticMeshElements ;
// If the static mesh is an occluder, check whether it covers enough of the screen to be used as an occluder.
if ( StaticMesh . bUseAsOccluder & & bDrawDepthOnly )
{
MarkMask | = EMarkMaskBits : : StaticMeshOccluderMapMask ;
}
bNeedsBatchVisibility = true ;
}
if ( MarkMask )
{
MarkMasks [ StaticMesh . Id ] = MarkMask ;
}
// Static meshes with a single element always draw, as if the mask were 0x1.
if ( bNeedsBatchVisibility & & StaticMesh . Elements . Num ( ) > 1 )
{
WriteView . StaticMeshBatchVisibility [ StaticMesh . Id ] = StaticMesh . VertexFactory - > GetStaticBatchElementVisibility ( View , & StaticMesh ) ;
}
}
}
}
static_assert ( sizeof ( WriteView . NumVisibleStaticMeshElements ) = = sizeof ( int32 ) , " Atomic is the wrong size " ) ;
FPlatformAtomics : : InterlockedAdd ( ( volatile int32 * ) & WriteView . NumVisibleStaticMeshElements , NumVisibleStaticMeshElements ) ;
}
void RenderThreadFinalize ( )
{
FViewInfo & WriteView = const_cast < FViewInfo & > ( View ) ;
2015-04-08 16:15:25 -04:00
2014-09-15 13:41:48 -04:00
for ( int32 Index = 0 ; Index < NotDrawRelevant . NumPrims ; Index + + )
{
WriteView . PrimitiveVisibilityMap [ NotDrawRelevant . Prims [ Index ] ] = false ;
}
2015-04-13 17:01:36 -04:00
WriteView . ShadingModelMaskInView | = CombinedShadingModelMask ;
2014-09-15 13:41:48 -04:00
VisibleEditorPrimitives . AppendTo ( WriteView . VisibleEditorPrimitives ) ;
VisibleDynamicPrimitives . AppendTo ( WriteView . VisibleDynamicPrimitives ) ;
WriteView . TranslucentPrimSet . AppendScenePrimitives ( SortedTranslucencyPrims . Prims , SortedTranslucencyPrims . NumPrims , SortedSeparateTranslucencyPrims . Prims , SortedSeparateTranslucencyPrims . NumPrims ) ;
DistortionPrimSet . AppendTo ( WriteView . DistortionPrimSet ) ;
CustomDepthSet . AppendTo ( WriteView . CustomDepthSet ) ;
for ( int32 Index = 0 ; Index < UpdateStaticMeshes . NumPrims ; Index + + )
{
UpdateStaticMeshes . Prims [ Index ] - > UpdateStaticMeshes ( RHICmdList ) ;
}
}
} ;
class FRelevancePacketAnyThreadTask
{
FRelevancePacket & Packet ;
ENamedThreads : : Type ThreadToUse ;
public :
2014-10-06 10:05:28 -04:00
FRelevancePacketAnyThreadTask ( FRelevancePacket & InPacket , ENamedThreads : : Type InThreadToUse )
: Packet ( InPacket )
2014-09-15 13:41:48 -04:00
, ThreadToUse ( InThreadToUse )
{
}
FORCEINLINE TStatId GetStatId ( ) const
{
RETURN_QUICK_DECLARE_CYCLE_STAT ( FRelevancePacketAnyThreadTask , STATGROUP_TaskGraphTasks ) ;
}
ENamedThreads : : Type GetDesiredThread ( )
{
return ThreadToUse ;
}
static ESubsequentsMode : : Type GetSubsequentsMode ( ) { return ESubsequentsMode : : TrackSubsequents ; }
void DoTask ( ENamedThreads : : Type CurrentThread , const FGraphEventRef & MyCompletionGraphEvent )
{
Packet . AnyThreadTask ( ) ;
}
} ;
class FRelevancePacketRenderThreadTask
{
FRelevancePacket & Packet ;
public :
2014-10-06 10:05:28 -04:00
FRelevancePacketRenderThreadTask ( FRelevancePacket & InPacket )
: Packet ( InPacket )
2014-09-15 13:41:48 -04:00
{
}
FORCEINLINE TStatId GetStatId ( ) const
{
RETURN_QUICK_DECLARE_CYCLE_STAT ( FRelevancePacketRenderThreadTask , STATGROUP_TaskGraphTasks ) ;
}
ENamedThreads : : Type GetDesiredThread ( )
{
return ENamedThreads : : RenderThread_Local ;
}
static ESubsequentsMode : : Type GetSubsequentsMode ( ) { return ESubsequentsMode : : TrackSubsequents ; }
void DoTask ( ENamedThreads : : Type CurrentThread , const FGraphEventRef & MyCompletionGraphEvent )
{
Packet . RenderThreadFinalize ( ) ;
}
} ;
static TAutoConsoleVariable < int32 > CVarParallelInitViews (
TEXT ( " r.ParallelInitViews " ) ,
2014-09-18 10:08:19 -04:00
# if WITH_EDITOR
2014-09-15 13:41:48 -04:00
0 ,
2014-09-18 10:08:19 -04:00
# else
1 ,
# endif
2014-11-20 12:36:32 -05:00
TEXT ( " Toggles parallel init views. " ) ,
2014-09-15 13:41:48 -04:00
ECVF_RenderThreadSafe
) ;
/**
* Computes view relevance for visible primitives in the view and adds them to
* appropriate per - view rendering lists .
* @ param Scene - The scene being rendered .
* @ param View - The view for which to compute relevance .
* @ param ViewBit - Bit mask : 1 < < ViewIndex where Views ( ViewIndex ) = = View .
* @ param OutRelevantStaticPrimitives - Upon return contains a list of relevant
* static primitives .
* callback for this view will have ViewBit set .
*/
static void ComputeAndMarkRelevanceForViewParallel (
FRHICommandListImmediate & RHICmdList ,
const FScene * Scene ,
FViewInfo & View ,
uint8 ViewBit ,
FPrimitiveViewMasks & OutHasDynamicMeshElementsMasks ,
FPrimitiveViewMasks & OutHasDynamicEditorMeshElementsMasks
)
{
check ( OutHasDynamicMeshElementsMasks . Num ( ) = = Scene - > Primitives . Num ( ) ) ;
const FMarkRelevantStaticMeshesForViewData ViewData ( View ) ;
int32 NumMesh = View . StaticMeshVisibilityMap . Num ( ) ;
check ( View . StaticMeshShadowDepthMap . Num ( ) = = NumMesh & & View . StaticMeshVelocityMap . Num ( ) = = NumMesh & & View . StaticMeshOccluderMap . Num ( ) = = NumMesh ) ;
uint8 * RESTRICT MarkMasks = ( uint8 * ) FMemStack : : Get ( ) . Alloc ( NumMesh + 31 , 8 ) ; // some padding to simplify the high speed transpose
FMemory : : Memzero ( MarkMasks , NumMesh + 31 ) ;
// I am going to do the render thread tasks in order, maybe that isn't necessary
FGraphEventRef LastRenderThread ;
{
FSceneSetBitIterator BitIt ( View . PrimitiveVisibilityMap ) ;
if ( BitIt )
{
int32 AnyThreadTasksPerRenderThreadTasks = 1 ;
if ( FApp : : ShouldUseThreadingForPerformance ( ) & & CVarParallelInitViews . GetValueOnRenderThread ( ) > 0 )
{
AnyThreadTasksPerRenderThreadTasks = FTaskGraphInterface : : Get ( ) . GetNumWorkerThreads ( ) + 1 ; // the idea is to put the render thread to work
}
// else AnyThreadTasksPerRenderThreadTasks == 1 means we never use a task thread
int32 WorkingAnyThreadTasksPerRenderThreadTasks = AnyThreadTasksPerRenderThreadTasks ;
FRelevancePacket * Packet = new ( FMemStack : : Get ( ) ) FRelevancePacket (
RHICmdList ,
Scene ,
View ,
ViewBit ,
ViewData ,
OutHasDynamicMeshElementsMasks ,
OutHasDynamicEditorMeshElementsMasks ,
MarkMasks ) ;
while ( 1 )
{
Packet - > Input . AddPrim ( BitIt . GetIndex ( ) ) ;
+ + BitIt ;
if ( Packet - > Input . IsFull ( ) | | ! BitIt )
{
// submit task
ENamedThreads : : Type ThreadToUse = ENamedThreads : : AnyThread ;
if ( ! - - WorkingAnyThreadTasksPerRenderThreadTasks )
{
WorkingAnyThreadTasksPerRenderThreadTasks = AnyThreadTasksPerRenderThreadTasks ;
ThreadToUse = ENamedThreads : : RenderThread_Local ;
}
FGraphEventArray RenderPrereqs ;
if ( LastRenderThread . GetReference ( ) )
{
RenderPrereqs . Add ( LastRenderThread ) ; // this puts the render thread ones in order
}
2014-10-06 10:05:28 -04:00
FGraphEventRef AnyThread = TGraphTask < FRelevancePacketAnyThreadTask > : : CreateTask ( nullptr , ENamedThreads : : RenderThread ) . ConstructAndDispatchWhenReady ( * Packet , ThreadToUse ) ;
2014-09-15 13:41:48 -04:00
RenderPrereqs . Add ( AnyThread ) ;
2014-10-06 10:05:28 -04:00
LastRenderThread = TGraphTask < FRelevancePacketRenderThreadTask > : : CreateTask ( & RenderPrereqs , ENamedThreads : : RenderThread ) . ConstructAndDispatchWhenReady ( * Packet ) ;
2014-09-15 13:41:48 -04:00
if ( ! BitIt )
{
break ;
}
else
{
Packet = new ( FMemStack : : Get ( ) ) FRelevancePacket (
RHICmdList ,
Scene ,
View ,
ViewBit ,
ViewData ,
OutHasDynamicMeshElementsMasks ,
OutHasDynamicEditorMeshElementsMasks ,
MarkMasks ) ;
}
}
}
}
}
if ( LastRenderThread . GetReference ( ) )
{
QUICK_SCOPE_CYCLE_COUNTER ( STAT_ComputeAndMarkRelevanceForViewParallel_Wait ) ;
FTaskGraphInterface : : Get ( ) . WaitUntilTaskCompletes ( LastRenderThread , ENamedThreads : : RenderThread_Local ) ;
}
QUICK_SCOPE_CYCLE_COUNTER ( STAT_ComputeAndMarkRelevanceForViewParallel_TransposeMeshBits ) ;
2015-04-24 11:20:23 -04:00
check ( View . StaticMeshVelocityMap . Num ( ) = = NumMesh & &
View . StaticMeshShadowDepthMap . Num ( ) = = NumMesh & &
View . StaticMeshVisibilityMap . Num ( ) = = NumMesh & &
View . StaticMeshOccluderMap . Num ( ) = = NumMesh & &
View . StaticMeshFadeOutDitheredLODMap . Num ( ) = = NumMesh & &
View . StaticMeshFadeInDitheredLODMap . Num ( ) = = NumMesh
) ;
2014-09-15 13:41:48 -04:00
uint32 * RESTRICT StaticMeshVisibilityMap_Words = View . StaticMeshVisibilityMap . GetData ( ) ;
uint32 * RESTRICT StaticMeshVelocityMap_Words = View . StaticMeshVelocityMap . GetData ( ) ;
uint32 * RESTRICT StaticMeshShadowDepthMap_Words = View . StaticMeshShadowDepthMap . GetData ( ) ;
uint32 * RESTRICT StaticMeshOccluderMap_Words = View . StaticMeshOccluderMap . GetData ( ) ;
2015-04-24 11:20:23 -04:00
uint32 * RESTRICT StaticMeshFadeOutDitheredLODMap_Words = View . StaticMeshFadeOutDitheredLODMap . GetData ( ) ;
uint32 * RESTRICT StaticMeshFadeInDitheredLODMap_Words = View . StaticMeshFadeInDitheredLODMap . GetData ( ) ;
2014-09-15 13:41:48 -04:00
const uint64 * RESTRICT MarkMasks64 = ( const uint64 * RESTRICT ) MarkMasks ;
const uint8 * RESTRICT MarkMasks8 = MarkMasks ;
for ( int32 BaseIndex = 0 ; BaseIndex < NumMesh ; BaseIndex + = 32 )
{
uint32 StaticMeshVisibilityMap_Word = 0 ;
uint32 StaticMeshVelocityMap_Word = 0 ;
uint32 StaticMeshShadowDepthMap_Word = 0 ;
uint32 StaticMeshOccluderMap_Word = 0 ;
2015-04-24 11:20:23 -04:00
uint32 StaticMeshFadeOutDitheredLODMap_Word = 0 ;
uint32 StaticMeshFadeInDitheredLODMap_Word = 0 ;
2014-09-15 13:41:48 -04:00
uint32 Mask = 1 ;
bool bAny = false ;
for ( int32 QWordIndex = 0 ; QWordIndex < 4 ; QWordIndex + + )
{
if ( * MarkMasks64 + + )
{
for ( int32 ByteIndex = 0 ; ByteIndex < 8 ; ByteIndex + + , Mask < < = 1 , MarkMasks8 + + )
{
uint8 MaskMask = * MarkMasks8 ;
StaticMeshVisibilityMap_Word | = ( MaskMask & EMarkMaskBits : : StaticMeshVisibilityMapMask ) ? Mask : 0 ;
StaticMeshVelocityMap_Word | = ( MaskMask & EMarkMaskBits : : StaticMeshVelocityMapMask ) ? Mask : 0 ;
StaticMeshShadowDepthMap_Word | = ( MaskMask & EMarkMaskBits : : StaticMeshShadowDepthMapMask ) ? Mask : 0 ;
StaticMeshOccluderMap_Word | = ( MaskMask & EMarkMaskBits : : StaticMeshOccluderMapMask ) ? Mask : 0 ;
2015-04-24 11:20:23 -04:00
StaticMeshFadeOutDitheredLODMap_Word | = ( MaskMask & EMarkMaskBits : : StaticMeshFadeOutDitheredLODMapMask ) ? Mask : 0 ;
StaticMeshFadeInDitheredLODMap_Word | = ( MaskMask & EMarkMaskBits : : StaticMeshFadeInDitheredLODMapMask ) ? Mask : 0 ;
2014-09-15 13:41:48 -04:00
}
bAny = true ;
}
else
{
MarkMasks8 + = 8 ;
Mask < < = 8 ;
}
}
if ( bAny )
{
2015-04-24 11:20:23 -04:00
checkSlow ( ! * StaticMeshVisibilityMap_Words & & ! * StaticMeshVelocityMap_Words & & ! * StaticMeshShadowDepthMap_Words & & ! * StaticMeshOccluderMap_Words & & ! * StaticMeshFadeOutDitheredLODMap_Words & & ! * StaticMeshFadeInDitheredLODMap_Words ) ;
2014-09-15 13:41:48 -04:00
* StaticMeshVisibilityMap_Words = StaticMeshVisibilityMap_Word ;
* StaticMeshVelocityMap_Words = StaticMeshVelocityMap_Word ;
* StaticMeshShadowDepthMap_Words = StaticMeshShadowDepthMap_Word ;
* StaticMeshOccluderMap_Words = StaticMeshOccluderMap_Word ;
2015-04-24 11:20:23 -04:00
* StaticMeshFadeOutDitheredLODMap_Words = StaticMeshFadeOutDitheredLODMap_Word ;
* StaticMeshFadeInDitheredLODMap_Words = StaticMeshFadeInDitheredLODMap_Word ;
2014-09-15 13:41:48 -04:00
}
StaticMeshVisibilityMap_Words + + ;
StaticMeshVelocityMap_Words + + ;
StaticMeshShadowDepthMap_Words + + ;
StaticMeshOccluderMap_Words + + ;
2015-04-24 11:20:23 -04:00
StaticMeshFadeOutDitheredLODMap_Words + + ;
StaticMeshFadeInDitheredLODMap_Words + + ;
2014-09-15 13:41:48 -04:00
}
}
2014-08-12 18:24:52 -04:00
void FSceneRenderer : : GatherDynamicMeshElements (
2015-04-10 11:23:49 -04:00
TArray < FViewInfo > & InViews ,
const FScene * InScene ,
const FSceneViewFamily & InViewFamily ,
2014-08-12 18:24:52 -04:00
const FPrimitiveViewMasks & HasDynamicMeshElementsMasks ,
const FPrimitiveViewMasks & HasDynamicEditorMeshElementsMasks ,
FMeshElementCollector & Collector )
{
SCOPE_CYCLE_COUNTER ( STAT_GetDynamicMeshElements ) ;
2015-04-10 11:23:49 -04:00
int32 NumPrimitives = InScene - > Primitives . Num ( ) ;
2014-08-12 18:24:52 -04:00
check ( HasDynamicMeshElementsMasks . Num ( ) = = NumPrimitives ) ;
{
Collector . ClearViewMeshArrays ( ) ;
2015-04-10 11:23:49 -04:00
for ( int32 ViewIndex = 0 ; ViewIndex < InViews . Num ( ) ; ViewIndex + + )
2014-08-12 18:24:52 -04:00
{
2015-04-10 11:23:49 -04:00
Collector . AddViewMeshArrays ( & InViews [ ViewIndex ] , & InViews [ ViewIndex ] . DynamicMeshElements , & InViews [ ViewIndex ] . SimpleElementCollector , InViewFamily . GetFeatureLevel ( ) ) ;
2014-08-12 18:24:52 -04:00
}
for ( int32 PrimitiveIndex = 0 ; PrimitiveIndex < NumPrimitives ; + + PrimitiveIndex )
{
const uint8 ViewMask = HasDynamicMeshElementsMasks [ PrimitiveIndex ] ;
if ( ViewMask ! = 0 )
{
2015-04-10 11:23:49 -04:00
FPrimitiveSceneInfo * PrimitiveSceneInfo = InScene - > Primitives [ PrimitiveIndex ] ;
2014-08-12 18:24:52 -04:00
Collector . SetPrimitive ( PrimitiveSceneInfo - > Proxy , PrimitiveSceneInfo - > DefaultDynamicHitProxyId ) ;
2015-04-10 11:23:49 -04:00
PrimitiveSceneInfo - > Proxy - > GetDynamicMeshElements ( InViewFamily . Views , InViewFamily , ViewMask , Collector ) ;
2014-08-12 18:24:52 -04:00
}
}
}
2014-08-14 11:24:24 -04:00
if ( GIsEditor )
2014-08-12 18:24:52 -04:00
{
Collector . ClearViewMeshArrays ( ) ;
2015-04-10 11:23:49 -04:00
for ( int32 ViewIndex = 0 ; ViewIndex < InViews . Num ( ) ; ViewIndex + + )
2014-08-12 18:24:52 -04:00
{
2015-04-10 11:23:49 -04:00
Collector . AddViewMeshArrays ( & InViews [ ViewIndex ] , & InViews [ ViewIndex ] . DynamicEditorMeshElements , & InViews [ ViewIndex ] . EditorSimpleElementCollector , InViewFamily . GetFeatureLevel ( ) ) ;
2014-08-12 18:24:52 -04:00
}
for ( int32 PrimitiveIndex = 0 ; PrimitiveIndex < NumPrimitives ; + + PrimitiveIndex )
{
const uint8 ViewMask = HasDynamicEditorMeshElementsMasks [ PrimitiveIndex ] ;
if ( ViewMask ! = 0 )
{
2015-04-10 11:23:49 -04:00
FPrimitiveSceneInfo * PrimitiveSceneInfo = InScene - > Primitives [ PrimitiveIndex ] ;
2014-08-12 18:24:52 -04:00
Collector . SetPrimitive ( PrimitiveSceneInfo - > Proxy , PrimitiveSceneInfo - > DefaultDynamicHitProxyId ) ;
2015-04-10 11:23:49 -04:00
PrimitiveSceneInfo - > Proxy - > GetDynamicMeshElements ( InViewFamily . Views , InViewFamily , ViewMask , Collector ) ;
2014-08-12 18:24:52 -04:00
}
}
}
}
2014-03-14 14:13:41 -04:00
static void MarkAllPrimitivesForReflectionProxyUpdate ( FScene * Scene )
{
2014-09-15 13:41:48 -04:00
QUICK_SCOPE_CYCLE_COUNTER ( STAT_MarkAllPrimitivesForReflectionProxyUpdate ) ;
2014-03-14 14:13:41 -04:00
if ( Scene - > ReflectionSceneData . bRegisteredReflectionCapturesHasChanged )
{
// Mark all primitives as needing an update
// Note: Only visible primitives will actually update their reflection proxy
for ( int32 PrimitiveIndex = 0 ; PrimitiveIndex < Scene - > Primitives . Num ( ) ; PrimitiveIndex + + )
{
Scene - > Primitives [ PrimitiveIndex ] - > bNeedsCachedReflectionCaptureUpdate = true ;
}
Scene - > ReflectionSceneData . bRegisteredReflectionCapturesHasChanged = false ;
}
}
/**
* Helper for InitViews to detect large camera movement , in both angle and position .
*/
static bool IsLargeCameraMovement ( FSceneView & View , const FMatrix & PrevViewMatrix , const FVector & PrevViewOrigin , float CameraRotationThreshold , float CameraTranslationThreshold )
{
float RotationThreshold = FMath : : Cos ( CameraRotationThreshold * PI / 180.0f ) ;
float ViewRightAngle = View . ViewMatrices . ViewMatrix . GetColumn ( 0 ) | PrevViewMatrix . GetColumn ( 0 ) ;
float ViewUpAngle = View . ViewMatrices . ViewMatrix . GetColumn ( 1 ) | PrevViewMatrix . GetColumn ( 1 ) ;
float ViewDirectionAngle = View . ViewMatrices . ViewMatrix . GetColumn ( 2 ) | PrevViewMatrix . GetColumn ( 2 ) ;
FVector Distance = FVector ( View . ViewMatrices . ViewOrigin ) - PrevViewOrigin ;
return
ViewRightAngle < RotationThreshold | |
ViewUpAngle < RotationThreshold | |
ViewDirectionAngle < RotationThreshold | |
Distance . SizeSquared ( ) > CameraTranslationThreshold * CameraTranslationThreshold ;
}
float Halton ( int32 Index , int32 Base )
{
float Result = 0.0f ;
float InvBase = 1.0f / Base ;
float Fraction = InvBase ;
while ( Index > 0 )
{
Result + = ( Index % Base ) * Fraction ;
Index / = Base ;
Fraction * = InvBase ;
}
return Result ;
}
2014-06-27 11:07:13 -04:00
void FSceneRenderer : : PreVisibilityFrameSetup ( FRHICommandListImmediate & RHICmdList )
2014-03-14 14:13:41 -04:00
{
2014-06-05 16:38:54 -04:00
// Notify the RHI we are beginning to render a scene.
2014-06-27 11:07:13 -04:00
RHICmdList . BeginScene ( ) ;
2014-06-05 16:38:54 -04:00
2014-03-14 14:13:41 -04:00
// Notify the FX system that the scene is about to perform visibility checks.
if ( Scene - > FXSystem )
{
Scene - > FXSystem - > PreInitViews ( ) ;
}
// Draw lines to lights affecting this mesh if its selected.
if ( ViewFamily . EngineShowFlags . LightInfluences )
{
for ( TArray < FPrimitiveSceneInfo * > : : TConstIterator It ( Scene - > Primitives ) ; It ; + + It )
{
const FPrimitiveSceneInfo * PrimitiveSceneInfo = * It ;
if ( PrimitiveSceneInfo - > Proxy - > IsSelected ( ) )
{
FLightPrimitiveInteraction * LightList = PrimitiveSceneInfo - > LightList ;
while ( LightList )
{
const FLightSceneInfo * LightSceneInfo = LightList - > GetLight ( ) ;
bool bDynamic = true ;
bool bRelevant = false ;
bool bLightMapped = true ;
bool bShadowMapped = false ;
PrimitiveSceneInfo - > Proxy - > GetLightRelevance ( LightSceneInfo - > Proxy , bDynamic , bRelevant , bLightMapped , bShadowMapped ) ;
if ( bRelevant )
{
// Draw blue for light-mapped lights and orange for dynamic lights
const FColor LineColor = bLightMapped ? FColor ( 0 , 140 , 255 ) : FColor ( 255 , 140 , 0 ) ;
for ( int32 ViewIndex = 0 ; ViewIndex < Views . Num ( ) ; ViewIndex + + )
{
FViewInfo & View = Views [ ViewIndex ] ;
FViewElementPDI LightInfluencesPDI ( & View , NULL ) ;
LightInfluencesPDI . DrawLine ( PrimitiveSceneInfo - > Proxy - > GetBounds ( ) . Origin , LightSceneInfo - > Proxy - > GetLightToWorld ( ) . GetOrigin ( ) , LineColor , SDPG_World ) ;
}
}
LightList = LightList - > GetNextLight ( ) ;
}
}
}
}
// Setup motion blur parameters (also check for camera movement thresholds)
for ( int32 ViewIndex = 0 ; ViewIndex < Views . Num ( ) ; ViewIndex + + )
{
FViewInfo & View = Views [ ViewIndex ] ;
FSceneViewState * ViewState = ( FSceneViewState * ) View . State ;
static bool bEnableTimeScale = true ;
2014-10-27 14:02:38 -04:00
// Once per render increment the occlusion frame counter.
if ( ViewState )
{
ViewState - > OcclusionFrameCounter + + ;
}
2014-03-14 14:13:41 -04:00
// HighResScreenshot should get best results so we don't do the occlusion optimization based on the former frame
extern bool GIsHighResScreenshot ;
const bool bIsHitTesting = ViewFamily . EngineShowFlags . HitProxies ;
2014-08-19 10:41:34 -04:00
if ( GIsHighResScreenshot | | ! DoOcclusionQueries ( FeatureLevel ) | | bIsHitTesting )
2014-03-14 14:13:41 -04:00
{
View . bDisableQuerySubmissions = true ;
View . bIgnoreExistingQueries = true ;
}
// set up the screen area for occlusion
float NumPossiblePixels = GSceneRenderTargets . UseDownsizedOcclusionQueries ( ) & & IsValidRef ( GSceneRenderTargets . GetSmallDepthSurface ( ) ) ?
( float ) View . ViewRect . Width ( ) / GSceneRenderTargets . GetSmallColorDepthDownsampleFactor ( ) * ( float ) View . ViewRect . Height ( ) / GSceneRenderTargets . GetSmallColorDepthDownsampleFactor ( ) :
View . ViewRect . Width ( ) * View . ViewRect . Height ( ) ;
View . OneOverNumPossiblePixels = NumPossiblePixels > 0.0 ? 1.0f / NumPossiblePixels : 0.0f ;
// Still need no jitter to be set for temporal feedback on SSR (it is enabled even when temporal AA is off).
View . TemporalJitterPixelsX = 0.0f ;
View . TemporalJitterPixelsY = 0.0f ;
if ( View . FinalPostProcessSettings . AntiAliasingMethod = = AAM_TemporalAA & & ViewState )
{
2014-06-25 05:47:33 -04:00
// Subpixel jitter for temporal AA
int32 TemporalAASamples = CVarTemporalAASamples . GetValueOnRenderThread ( ) ;
2014-03-14 14:13:41 -04:00
if ( TemporalAASamples > 1 )
{
float SampleX , SampleY ;
2014-05-08 09:05:50 -04:00
if ( Scene - > GetFeatureLevel ( ) < ERHIFeatureLevel : : SM4 )
2014-03-14 14:13:41 -04:00
{
// Only support 2 samples for mobile temporal AA.
TemporalAASamples = 2 ;
}
if ( TemporalAASamples = = 2 )
{
#if 0
// 2xMSAA
// Pattern docs: http://msdn.microsoft.com/en-us/library/windows/desktop/ff476218(v=vs.85).aspx
// N.
// .S
float SamplesX [ ] = { - 4.0f / 16.0f , 4.0 / 16.0f } ;
float SamplesY [ ] = { - 4.0f / 16.0f , 4.0 / 16.0f } ;
# else
// This pattern is only used for mobile.
// Shift to reduce blur.
float SamplesX [ ] = { - 8.0f / 16.0f , 0.0 / 16.0f } ;
float SamplesY [ ] = { /* - */ 0.0f / 16.0f , 8.0 / 16.0f } ;
# endif
2015-04-16 16:29:54 -04:00
ViewState - > SetupTemporalAA ( ARRAY_COUNT ( SamplesX ) , ViewFamily ) ;
2014-03-14 14:13:41 -04:00
uint32 Index = ViewState - > GetCurrentTemporalAASampleIndex ( ) ;
SampleX = SamplesX [ Index ] ;
SampleY = SamplesY [ Index ] ;
}
else if ( TemporalAASamples = = 3 )
{
// 3xMSAA
// A..
// ..B
// .C.
// Rolling circle pattern (A,B,C).
float SamplesX [ ] = { - 2.0f / 3.0f , 2.0 / 3.0f , 0.0 / 3.0f } ;
float SamplesY [ ] = { - 2.0f / 3.0f , 0.0 / 3.0f , 2.0 / 3.0f } ;
2015-04-16 16:29:54 -04:00
ViewState - > SetupTemporalAA ( ARRAY_COUNT ( SamplesX ) , ViewFamily ) ;
2014-03-14 14:13:41 -04:00
uint32 Index = ViewState - > GetCurrentTemporalAASampleIndex ( ) ;
SampleX = SamplesX [ Index ] ;
SampleY = SamplesY [ Index ] ;
}
else if ( TemporalAASamples = = 4 )
{
// 4xMSAA
// Pattern docs: http://msdn.microsoft.com/en-us/library/windows/desktop/ff476218(v=vs.85).aspx
// .N..
// ...E
// W...
// ..S.
// Rolling circle pattern (N,E,S,W).
float SamplesX [ ] = { - 2.0f / 16.0f , 6.0 / 16.0f , 2.0 / 16.0f , - 6.0 / 16.0f } ;
float SamplesY [ ] = { - 6.0f / 16.0f , - 2.0 / 16.0f , 6.0 / 16.0f , 2.0 / 16.0f } ;
2015-04-16 16:29:54 -04:00
ViewState - > SetupTemporalAA ( ARRAY_COUNT ( SamplesX ) , ViewFamily ) ;
2014-03-14 14:13:41 -04:00
uint32 Index = ViewState - > GetCurrentTemporalAASampleIndex ( ) ;
SampleX = SamplesX [ Index ] ;
SampleY = SamplesY [ Index ] ;
}
else if ( TemporalAASamples = = 5 )
{
// Compressed 4 sample pattern on same vertical and horizontal line (less temporal flicker).
// Compressed 1/2 works better than correct 2/3 (reduced temporal flicker).
// . N .
// W . E
// . S .
// Rolling circle pattern (N,E,S,W).
float SamplesX [ ] = { 0.0f / 2.0f , 1.0 / 2.0f , 0.0 / 2.0f , - 1.0 / 2.0f } ;
float SamplesY [ ] = { - 1.0f / 2.0f , 0.0 / 2.0f , 1.0 / 2.0f , 0.0 / 2.0f } ;
2015-04-16 16:29:54 -04:00
ViewState - > SetupTemporalAA ( ARRAY_COUNT ( SamplesX ) , ViewFamily ) ;
2014-03-14 14:13:41 -04:00
uint32 Index = ViewState - > GetCurrentTemporalAASampleIndex ( ) ;
SampleX = SamplesX [ Index ] ;
SampleY = SamplesY [ Index ] ;
}
else if ( TemporalAASamples = = 8 )
{
// This works better than various orderings of 8xMSAA.
2015-04-16 16:29:54 -04:00
ViewState - > SetupTemporalAA ( 8 , ViewFamily ) ;
2014-03-14 14:13:41 -04:00
uint32 Index = ViewState - > GetCurrentTemporalAASampleIndex ( ) ;
SampleX = Halton ( Index , 2 ) - 0.5f ;
SampleY = Halton ( Index , 3 ) - 0.5f ;
}
else
{
// More than 8 samples can improve quality.
2015-04-16 16:29:54 -04:00
ViewState - > SetupTemporalAA ( TemporalAASamples , ViewFamily ) ;
2014-03-14 14:13:41 -04:00
uint32 Index = ViewState - > GetCurrentTemporalAASampleIndex ( ) ;
SampleX = Halton ( Index , 2 ) - 0.5f ;
SampleY = Halton ( Index , 3 ) - 0.5f ;
}
2015-04-20 15:50:25 -04:00
View . ViewMatrices . TemporalAASample . X = SampleX ;
View . ViewMatrices . TemporalAASample . Y = SampleY ;
2014-03-14 14:13:41 -04:00
2015-04-20 15:50:25 -04:00
View . ViewMatrices . ProjMatrix . M [ 2 ] [ 0 ] + = View . ViewMatrices . TemporalAASample . X * 2.0f / View . ViewRect . Width ( ) ;
View . ViewMatrices . ProjMatrix . M [ 2 ] [ 1 ] + = View . ViewMatrices . TemporalAASample . Y * 2.0f / View . ViewRect . Height ( ) ; ;
2014-03-14 14:13:41 -04:00
// Compute the view projection matrix and its inverse.
View . ViewProjectionMatrix = View . ViewMatrices . ViewMatrix * View . ViewMatrices . ProjMatrix ;
View . InvViewProjectionMatrix = View . ViewMatrices . GetInvProjMatrix ( ) * View . InvViewMatrix ;
/** The view transform, starting from world-space points translated by -ViewOrigin. */
FMatrix TranslatedViewMatrix = FTranslationMatrix ( - View . ViewMatrices . PreViewTranslation ) * View . ViewMatrices . ViewMatrix ;
// Compute a transform from view origin centered world-space to clip space.
View . ViewMatrices . TranslatedViewProjectionMatrix = TranslatedViewMatrix * View . ViewMatrices . ProjMatrix ;
2014-08-13 15:29:41 -04:00
View . ViewMatrices . InvTranslatedViewProjectionMatrix = View . ViewMatrices . TranslatedViewProjectionMatrix . Inverse ( ) ;
2014-03-14 14:13:41 -04:00
}
}
else if ( ViewState )
{
// no TemporalAA
2015-04-16 16:29:54 -04:00
ViewState - > SetupTemporalAA ( 1 , ViewFamily ) ;
2014-03-14 14:13:41 -04:00
}
if ( ViewState )
{
2014-07-24 01:38:09 -04:00
// In case world origin was rebased, reset previous view transformations
if ( View . bOriginOffsetThisFrame )
{
ViewState - > PrevViewMatrices = View . ViewMatrices ;
ViewState - > PendingPrevViewMatrices = View . ViewMatrices ;
}
2014-03-14 14:13:41 -04:00
// determine if we are initializing or we should reset the persistent state
const float DeltaTime = View . Family - > CurrentRealTime - ViewState - > LastRenderTime ;
const bool bFirstFrameOrTimeWasReset = DeltaTime < - 0.0001f | | ViewState - > LastRenderTime < 0.0001f ;
// detect conditions where we should reset occlusion queries
if ( bFirstFrameOrTimeWasReset | |
ViewState - > LastRenderTime + GEngine - > PrimitiveProbablyVisibleTime < View . Family - > CurrentRealTime | |
View . bCameraCut | |
IsLargeCameraMovement (
View ,
ViewState - > PrevViewMatrixForOcclusionQuery ,
ViewState - > PrevViewOriginForOcclusionQuery ,
GEngine - > CameraRotationThreshold , GEngine - > CameraTranslationThreshold ) )
{
View . bIgnoreExistingQueries = true ;
View . bDisableDistanceBasedFadeTransitions = true ;
}
ViewState - > PrevViewMatrixForOcclusionQuery = View . ViewMatrices . ViewMatrix ;
ViewState - > PrevViewOriginForOcclusionQuery = View . ViewMatrices . ViewOrigin ;
// store old view matrix and detect conditions where we should reset motion blur
{
bool bResetCamera = bFirstFrameOrTimeWasReset
| | View . bCameraCut
| | IsLargeCameraMovement ( View , ViewState - > PrevViewMatrices . ViewMatrix , ViewState - > PrevViewMatrices . ViewOrigin , 45.0f , 10000.0f ) ;
if ( bResetCamera )
{
ViewState - > PrevViewMatrices = View . ViewMatrices ;
ViewState - > PendingPrevViewMatrices = ViewState - > PrevViewMatrices ;
// PT: If the motion blur shader is the last shader in the post-processing chain then it is the one that is
// adjusting for the viewport offset. So it is always required and we can't just disable the work the
// shader does. The correct fix would be to disable the effect when we don't need it and to properly mark
// the uber-postprocessing effect as the last effect in the chain.
View . bPrevTransformsReset = true ;
}
else
{
// check for pause so we can keep motion blur in paused mode (doesn't work in editor)
2015-04-16 16:29:54 -04:00
if ( ! ViewFamily . bWorldIsPaused )
2014-03-14 14:13:41 -04:00
{
2015-04-27 17:12:21 -04:00
ViewState - > PrevViewMatrices = ViewState - > PendingPrevViewMatrices ;
2014-03-14 14:13:41 -04:00
// pending is needed as we are in init view and still need to render.
ViewState - > PendingPrevViewMatrices = View . ViewMatrices ;
}
}
// we don't use DeltaTime as it can be 0 (in editor) and is computed by subtracting floats (loses precision over time)
// Clamp DeltaWorldTime to reasonable values for the purposes of motion blur, things like TimeDilation can make it very small
2015-04-16 16:29:54 -04:00
if ( ! ViewFamily . bWorldIsPaused )
2014-03-14 14:13:41 -04:00
{
ViewState - > MotionBlurTimeScale = bEnableTimeScale ? ( 1.0f / ( FMath : : Max ( View . Family - > DeltaWorldTime , .00833f ) * 30.0f ) ) : 1.0f ;
}
View . PrevViewMatrices = ViewState - > PrevViewMatrices ;
View . PrevViewProjMatrix = ViewState - > PrevViewMatrices . GetViewProjMatrix ( ) ;
View . PrevViewRotationProjMatrix = ViewState - > PrevViewMatrices . GetViewRotationProjMatrix ( ) ;
}
ViewState - > PrevFrameNumber = ViewState - > PendingPrevFrameNumber ;
2014-12-02 13:48:22 -05:00
ViewState - > PendingPrevFrameNumber = View . Family - > FrameNumber ;
2014-03-14 14:13:41 -04:00
// This finishes the update of view state
ViewState - > UpdateLastRenderTime ( * View . Family ) ;
2014-12-04 12:41:25 -05:00
ViewState - > UpdateTemporalLODTransition ( View ) ;
2014-10-02 14:53:35 -04:00
}
2014-03-14 14:13:41 -04:00
}
}
2014-06-27 11:07:13 -04:00
void FSceneRenderer : : ComputeViewVisibility ( FRHICommandListImmediate & RHICmdList )
2014-03-14 14:13:41 -04:00
{
SCOPE_CYCLE_COUNTER ( STAT_ViewVisibilityTime ) ;
STAT ( int32 NumProcessedPrimitives = 0 ) ;
STAT ( int32 NumCulledPrimitives = 0 ) ;
STAT ( int32 NumOccludedPrimitives = 0 ) ;
// Allocate the visible light info.
if ( Scene - > Lights . GetMaxIndex ( ) > 0 )
{
VisibleLightInfos . AddZeroed ( Scene - > Lights . GetMaxIndex ( ) ) ;
}
int32 NumPrimitives = Scene - > Primitives . Num ( ) ;
float CurrentRealTime = ViewFamily . CurrentRealTime ;
2014-08-12 18:24:52 -04:00
FPrimitiveViewMasks HasDynamicMeshElementsMasks ;
HasDynamicMeshElementsMasks . AddZeroed ( NumPrimitives ) ;
FPrimitiveViewMasks HasDynamicEditorMeshElementsMasks ;
if ( GIsEditor )
{
HasDynamicEditorMeshElementsMasks . AddZeroed ( NumPrimitives ) ;
}
2014-03-14 14:13:41 -04:00
uint8 ViewBit = 0x1 ;
for ( int32 ViewIndex = 0 ; ViewIndex < Views . Num ( ) ; + + ViewIndex , ViewBit < < = 1 )
{
STAT ( NumProcessedPrimitives + = NumPrimitives ) ;
FViewInfo & View = Views [ ViewIndex ] ;
FSceneViewState * ViewState = ( FSceneViewState * ) View . State ;
// Allocate the view's visibility maps.
View . PrimitiveVisibilityMap . Init ( false , Scene - > Primitives . Num ( ) ) ;
View . PrimitiveDefinitelyUnoccludedMap . Init ( false , Scene - > Primitives . Num ( ) ) ;
View . PotentiallyFadingPrimitiveMap . Init ( false , Scene - > Primitives . Num ( ) ) ;
View . PrimitiveFadeUniformBuffers . AddZeroed ( Scene - > Primitives . Num ( ) ) ;
View . StaticMeshVisibilityMap . Init ( false , Scene - > StaticMeshes . GetMaxIndex ( ) ) ;
View . StaticMeshOccluderMap . Init ( false , Scene - > StaticMeshes . GetMaxIndex ( ) ) ;
2015-04-24 11:20:23 -04:00
View . StaticMeshFadeOutDitheredLODMap . Init ( false , Scene - > StaticMeshes . GetMaxIndex ( ) ) ;
View . StaticMeshFadeInDitheredLODMap . Init ( false , Scene - > StaticMeshes . GetMaxIndex ( ) ) ;
2014-03-14 14:13:41 -04:00
View . StaticMeshVelocityMap . Init ( false , Scene - > StaticMeshes . GetMaxIndex ( ) ) ;
View . StaticMeshShadowDepthMap . Init ( false , Scene - > StaticMeshes . GetMaxIndex ( ) ) ;
View . StaticMeshBatchVisibility . AddZeroed ( Scene - > StaticMeshes . GetMaxIndex ( ) ) ;
View . VisibleLightInfos . Empty ( Scene - > Lights . GetMaxIndex ( ) ) ;
for ( int32 LightIndex = 0 ; LightIndex < Scene - > Lights . GetMaxIndex ( ) ; LightIndex + + )
{
if ( LightIndex + 2 < Scene - > Lights . GetMaxIndex ( ) )
{
if ( LightIndex > 2 )
{
FLUSH_CACHE_LINE ( & View . VisibleLightInfos ( LightIndex - 2 ) ) ;
}
// @todo optimization These prefetches cause asserts since LightIndex > View.VisibleLightInfos.Num() - 1
//FPlatformMisc::Prefetch(&View.VisibleLightInfos[LightIndex+2]);
//FPlatformMisc::Prefetch(&View.VisibleLightInfos[LightIndex+1]);
}
new ( View . VisibleLightInfos ) FVisibleLightViewInfo ( ) ;
}
View . PrimitiveViewRelevanceMap . Empty ( Scene - > Primitives . Num ( ) ) ;
View . PrimitiveViewRelevanceMap . AddZeroed ( Scene - > Primitives . Num ( ) ) ;
// If this is the visibility-parent of other views, reset its ParentPrimitives list.
const bool bIsParent = ViewState & & ViewState - > IsViewParent ( ) ;
if ( bIsParent )
{
2015-04-09 16:42:44 -04:00
// PVS-Studio does not understand the validation of ViewState above, so we're disabling
// its warning that ViewState may be null:
ViewState - > ParentPrimitives . Empty ( ) ; //-V595
2014-03-14 14:13:41 -04:00
}
if ( ViewState )
{
SCOPE_CYCLE_COUNTER ( STAT_DecompressPrecomputedOcclusion ) ;
View . PrecomputedVisibilityData = ViewState - > GetPrecomputedVisibilityData ( View , Scene ) ;
}
else
{
View . PrecomputedVisibilityData = NULL ;
}
if ( View . PrecomputedVisibilityData )
{
bUsedPrecomputedVisibility = true ;
}
bool bNeedsFrustumCulling = true ;
// Development builds sometimes override frustum culling, e.g. dependent views in the editor.
# if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
if ( ViewState )
{
# if WITH_EDITOR
// For visibility child views, check if the primitive was visible in the parent view.
const FSceneViewState * const ViewParent = ( FSceneViewState * ) ViewState - > GetViewParent ( ) ;
if ( ViewParent )
{
bNeedsFrustumCulling = false ;
for ( FSceneBitArray : : FIterator BitIt ( View . PrimitiveVisibilityMap ) ; BitIt ; + + BitIt )
{
if ( ViewParent - > ParentPrimitives . Contains ( Scene - > PrimitiveComponentIds [ BitIt . GetIndex ( ) ] ) )
{
BitIt . GetValue ( ) = true ;
}
}
}
# endif
// For views with frozen visibility, check if the primitive is in the frozen visibility set.
if ( ViewState - > bIsFrozen )
{
bNeedsFrustumCulling = false ;
for ( FSceneBitArray : : FIterator BitIt ( View . PrimitiveVisibilityMap ) ; BitIt ; + + BitIt )
{
if ( ViewState - > FrozenPrimitives . Contains ( Scene - > PrimitiveComponentIds [ BitIt . GetIndex ( ) ] ) )
{
BitIt . GetValue ( ) = true ;
}
}
}
}
# endif
// Most views use standard frustum culling.
if ( bNeedsFrustumCulling )
{
2014-10-02 14:53:35 -04:00
int32 NumCulledPrimitivesForView ;
if ( View . CustomVisibilityQuery & & View . CustomVisibilityQuery - > Prepare ( ) )
{
NumCulledPrimitivesForView = FrustumCull < true > ( Scene , View ) ;
}
else
{
NumCulledPrimitivesForView = FrustumCull < false > ( Scene , View ) ;
}
2014-03-14 14:13:41 -04:00
STAT ( NumCulledPrimitives + = NumCulledPrimitivesForView ) ;
UpdatePrimitiveFading ( Scene , View ) ;
}
// If any primitives are explicitly hidden, remove them now.
if ( View . HiddenPrimitives . Num ( ) )
{
for ( FSceneSetBitIterator BitIt ( View . PrimitiveVisibilityMap ) ; BitIt ; + + BitIt )
{
if ( View . HiddenPrimitives . Contains ( Scene - > PrimitiveComponentIds [ BitIt . GetIndex ( ) ] ) )
{
View . PrimitiveVisibilityMap . AccessCorrespondingBit ( BitIt ) = false ;
}
}
}
2014-06-03 15:53:13 -04:00
if ( View . bStaticSceneOnly )
2014-03-14 14:13:41 -04:00
{
for ( FSceneSetBitIterator BitIt ( View . PrimitiveVisibilityMap ) ; BitIt ; + + BitIt )
{
// Reflection captures should only capture objects that won't move, since reflection captures won't update at runtime
if ( ! Scene - > Primitives [ BitIt . GetIndex ( ) ] - > Proxy - > HasStaticLighting ( ) )
{
View . PrimitiveVisibilityMap . AccessCorrespondingBit ( BitIt ) = false ;
}
}
}
// Cull small objects in wireframe in ortho views
// This is important for performance in the editor because wireframe disables any kind of occlusion culling
if ( View . Family - > EngineShowFlags . Wireframe )
{
float ScreenSizeScale = FMath : : Max ( View . ViewMatrices . ProjMatrix . M [ 0 ] [ 0 ] * View . ViewRect . Width ( ) , View . ViewMatrices . ProjMatrix . M [ 1 ] [ 1 ] * View . ViewRect . Height ( ) ) ;
for ( FSceneSetBitIterator BitIt ( View . PrimitiveVisibilityMap ) ; BitIt ; + + BitIt )
{
if ( ScreenSizeScale * Scene - > PrimitiveBounds [ BitIt . GetIndex ( ) ] . SphereRadius < = GWireframeCullThreshold )
{
View . PrimitiveVisibilityMap . AccessCorrespondingBit ( BitIt ) = false ;
}
}
}
// Occlusion cull for all primitives in the view frustum, but not in wireframe.
if ( ! View . Family - > EngineShowFlags . Wireframe )
{
2014-06-27 11:07:13 -04:00
int32 NumOccludedPrimitivesInView = OcclusionCull ( RHICmdList , Scene , View ) ;
2014-03-14 14:13:41 -04:00
STAT ( NumOccludedPrimitives + = NumOccludedPrimitivesInView ) ;
}
2015-01-22 11:05:14 -05:00
// visibility test is done, so now build the hidden flags based on visibility set up
bool bLODSceneTreeActive = Scene - > SceneLODHierarchy . IsActive ( ) ;
FSceneBitArray PrimitiveHiddenByLODMap ;
if ( bLODSceneTreeActive )
{
PrimitiveHiddenByLODMap . Init ( false , View . PrimitiveVisibilityMap . Num ( ) ) ;
Scene - > SceneLODHierarchy . PopulateHiddenFlags ( View , PrimitiveHiddenByLODMap ) ;
// now iterate through turn off visibility if hidden by LOD
for ( FSceneSetBitIterator BitIt ( PrimitiveHiddenByLODMap ) ; BitIt ; + + BitIt )
{
if ( PrimitiveHiddenByLODMap . AccessCorrespondingBit ( BitIt ) )
{
View . PrimitiveVisibilityMap . AccessCorrespondingBit ( BitIt ) = false ;
}
}
}
2014-03-14 14:13:41 -04:00
MarkAllPrimitivesForReflectionProxyUpdate ( Scene ) ;
Scene - > ConditionalMarkStaticMeshElementsForUpdate ( ) ;
2014-09-15 13:41:48 -04:00
{
SCOPE_CYCLE_COUNTER ( STAT_ViewRelevance ) ;
2015-04-24 11:20:23 -04:00
ComputeAndMarkRelevanceForViewParallel ( RHICmdList , Scene , View , ViewBit , HasDynamicMeshElementsMasks , HasDynamicEditorMeshElementsMasks ) ;
2015-01-22 11:05:14 -05:00
if ( bLODSceneTreeActive )
{
for ( FSceneBitArray : : FIterator BitIt ( PrimitiveHiddenByLODMap ) ; BitIt ; + + BitIt )
{
View . PrimitiveViewRelevanceMap [ BitIt . GetIndex ( ) ] . bInitializedThisFrame = true ;
}
}
2014-09-15 13:41:48 -04:00
}
2014-03-14 14:13:41 -04:00
# if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
// Store the primitive for parent occlusion rendering.
if ( FPlatformProperties : : SupportsWindowedMode ( ) & & ViewState & & ViewState - > IsViewParent ( ) )
{
for ( FSceneDualSetBitIterator BitIt ( View . PrimitiveVisibilityMap , View . PrimitiveDefinitelyUnoccludedMap ) ; BitIt ; + + BitIt )
{
ViewState - > ParentPrimitives . Add ( Scene - > PrimitiveComponentIds [ BitIt . GetIndex ( ) ] ) ;
}
}
# endif
# if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
// if we are freezing the scene, then remember the primitives that are rendered.
if ( ViewState & & ViewState - > bIsFreezing )
{
for ( FSceneSetBitIterator BitIt ( View . PrimitiveVisibilityMap ) ; BitIt ; + + BitIt )
{
ViewState - > FrozenPrimitives . Add ( Scene - > PrimitiveComponentIds [ BitIt . GetIndex ( ) ] ) ;
}
}
# endif
}
2014-11-25 17:56:43 -05:00
GatherDynamicMeshElements ( Views , Scene , ViewFamily , HasDynamicMeshElementsMasks , HasDynamicEditorMeshElementsMasks , MeshCollector ) ;
2014-03-14 14:13:41 -04:00
INC_DWORD_STAT_BY ( STAT_ProcessedPrimitives , NumProcessedPrimitives ) ;
INC_DWORD_STAT_BY ( STAT_CulledPrimitives , NumCulledPrimitives ) ;
INC_DWORD_STAT_BY ( STAT_OccludedPrimitives , NumOccludedPrimitives ) ;
}
void FSceneRenderer : : PostVisibilityFrameSetup ( )
{
for ( int32 ViewIndex = 0 ; ViewIndex < Views . Num ( ) ; ViewIndex + + )
{
FViewInfo & View = Views [ ViewIndex ] ;
// sort the translucent primitives
View . TranslucentPrimSet . SortPrimitives ( ) ;
2014-12-10 12:13:56 -05:00
if ( View . State )
{
( ( FSceneViewState * ) View . State ) - > TrimHistoryRenderTargets ( Scene ) ;
}
2014-03-14 14:13:41 -04:00
}
bool bCheckLightShafts = false ;
2014-09-12 17:21:49 -04:00
if ( Scene - > GetFeatureLevel ( ) < = ERHIFeatureLevel : : ES3_1 )
2014-03-14 14:13:41 -04:00
{
// Clear the mobile light shaft data.
for ( int32 ViewIndex = 0 ; ViewIndex < Views . Num ( ) ; ViewIndex + + )
{
FViewInfo & View = Views [ ViewIndex ] ;
View . bLightShaftUse = false ;
View . LightShaftCenter . X = 0.0f ;
View . LightShaftCenter . Y = 0.0f ;
View . LightShaftColorMask = FLinearColor ( 0.0f , 0.0f , 0.0f ) ;
View . LightShaftColorApply = FLinearColor ( 0.0f , 0.0f , 0.0f ) ;
}
bCheckLightShafts = ViewFamily . EngineShowFlags . LightShafts & & CVarLightShaftQuality . GetValueOnRenderThread ( ) > 0 ;
}
if ( ViewFamily . EngineShowFlags . HitProxies = = 0 )
{
Scene - > IndirectLightingCache . UpdateCache ( Scene , * this , true ) ;
}
// determine visibility of each light
for ( TSparseArray < FLightSceneInfoCompact > : : TConstIterator LightIt ( Scene - > Lights ) ; LightIt ; + + LightIt )
{
const FLightSceneInfoCompact & LightSceneInfoCompact = * LightIt ;
const FLightSceneInfo * LightSceneInfo = LightSceneInfoCompact . LightSceneInfo ;
// view frustum cull lights in each view
for ( int32 ViewIndex = 0 ; ViewIndex < Views . Num ( ) ; ViewIndex + + )
{
const FLightSceneProxy * Proxy = LightSceneInfo - > Proxy ;
FViewInfo & View = Views [ ViewIndex ] ;
FVisibleLightViewInfo & VisibleLightViewInfo = View . VisibleLightInfos [ LightIt . GetIndex ( ) ] ;
// dir lights are always visible, and point/spot only if in the frustum
if ( Proxy - > GetLightType ( ) = = LightType_Point
| | Proxy - > GetLightType ( ) = = LightType_Spot )
{
const float Radius = Proxy - > GetRadius ( ) ;
if ( View . ViewFrustum . IntersectSphere ( Proxy - > GetOrigin ( ) , Radius ) )
{
FSphere Bounds = Proxy - > GetBoundingSphere ( ) ;
float DistanceSquared = ( Bounds . Center - View . ViewMatrices . ViewOrigin ) . SizeSquared ( ) ;
const bool bDrawLight = FMath : : Square ( FMath : : Min ( 0.0002f , GMinScreenRadiusForLights / Bounds . W ) * View . LODDistanceFactor ) * DistanceSquared < 1.0f ;
VisibleLightViewInfo . bInViewFrustum = bDrawLight ;
}
}
else
{
VisibleLightViewInfo . bInViewFrustum = true ;
2014-08-15 16:23:58 -04:00
static const auto CVarMobileMSAA = IConsoleManager : : Get ( ) . FindTConsoleVariableDataInt ( TEXT ( " r.MobileMSAA " ) ) ;
bool bNotMobileMSAA = ! ( CVarMobileMSAA ? CVarMobileMSAA - > GetValueOnRenderThread ( ) > 1 : false ) ;
2014-03-14 14:13:41 -04:00
// Setup single sun-shaft from direction lights for mobile.
if ( bCheckLightShafts & & LightSceneInfo - > bEnableLightShaftBloom )
{
// Find directional light for sun shafts.
// Tweaked values from UE3 implementation.
const float PointLightFadeDistanceIncrease = 200.0f ;
const float PointLightRadiusFadeFactor = 5.0f ;
const FVector WorldSpaceBlurOrigin = LightSceneInfo - > Proxy - > GetPosition ( ) ;
// Transform into post projection space
FVector4 ProjectedBlurOrigin = View . WorldToScreen ( WorldSpaceBlurOrigin ) ;
const float DistanceToBlurOrigin = ( View . ViewMatrices . ViewOrigin - WorldSpaceBlurOrigin ) . Size ( ) + PointLightFadeDistanceIncrease ;
// Don't render if the light's origin is behind the view
if ( ProjectedBlurOrigin . W > = 0.0f
// Don't render point lights that have completely faded out
& & ( LightSceneInfo - > Proxy - > GetLightType ( ) = = LightType_Directional
| | DistanceToBlurOrigin < LightSceneInfo - > Proxy - > GetRadius ( ) * PointLightRadiusFadeFactor ) )
{
2014-08-15 16:23:58 -04:00
View . bLightShaftUse = bNotMobileMSAA ;
2014-03-14 14:13:41 -04:00
View . LightShaftCenter . X = ProjectedBlurOrigin . X / ProjectedBlurOrigin . W ;
View . LightShaftCenter . Y = ProjectedBlurOrigin . Y / ProjectedBlurOrigin . W ;
// TODO: Might want to hookup different colors for these.
View . LightShaftColorMask = LightSceneInfo - > BloomTint ;
View . LightShaftColorApply = LightSceneInfo - > BloomTint ;
}
}
}
2015-02-10 16:13:42 -05:00
// Draw shapes for reflection captures
if ( View . bIsReflectionCapture & & VisibleLightViewInfo . bInViewFrustum & & Proxy - > HasStaticLighting ( ) & & Proxy - > GetLightType ( ) ! = LightType_Directional )
{
FVector Origin = Proxy - > GetOrigin ( ) ;
FVector ToLight = Origin - View . ViewMatrices . ViewOrigin ;
float DistanceSqr = ToLight | ToLight ;
float Radius = Proxy - > GetRadius ( ) ;
if ( DistanceSqr < Radius * Radius )
{
FVector4 PositionAndInvRadius ;
FVector4 ColorAndFalloffExponent ;
FVector Direction ;
FVector2D SpotAngles ;
float SourceRadius ;
float SourceLength ;
float MinRoughness ;
Proxy - > GetParameters ( PositionAndInvRadius , ColorAndFalloffExponent , Direction , SpotAngles , SourceRadius , SourceLength , MinRoughness ) ;
// Force to be at least 0.75 pixels
float CubemapSize = 128.0f ;
float Distance = FMath : : Sqrt ( DistanceSqr ) ;
float MinRadius = Distance * 0.75f / CubemapSize ;
SourceRadius = FMath : : Max ( MinRadius , SourceRadius ) ;
// Snap to cubemap pixel center to reduce aliasing
FVector Scale = ToLight . GetAbs ( ) ;
int32 MaxComponent = Scale . X > Scale . Y ? ( Scale . X > Scale . Z ? 0 : 2 ) : ( Scale . Y > Scale . Z ? 1 : 2 ) ;
for ( int32 k = 1 ; k < 3 ; k + + )
{
float Projected = ToLight [ ( MaxComponent + k ) % 3 ] / Scale [ MaxComponent ] ;
float Quantized = ( FMath : : RoundToFloat ( Projected * ( 0.5f * CubemapSize ) - 0.5f ) + 0.5f ) / ( 0.5f * CubemapSize ) ;
ToLight [ ( MaxComponent + k ) % 3 ] = Quantized * Scale [ MaxComponent ] ;
}
Origin = ToLight + View . ViewMatrices . ViewOrigin ;
FLinearColor Color ( ColorAndFalloffExponent ) ;
if ( Proxy - > IsInverseSquared ( ) )
{
float LightRadiusMask = FMath : : Square ( 1.0f - FMath : : Square ( DistanceSqr * FMath : : Square ( PositionAndInvRadius . W ) ) ) ;
Color * = LightRadiusMask ;
// Correction for lumen units
Color * = 16.0f ;
}
else
{
// Remove inverse square falloff
Color * = DistanceSqr + 1.0f ;
// Apply falloff
Color * = FMath : : Pow ( 1.0f - DistanceSqr * FMath : : Square ( PositionAndInvRadius . W ) , ColorAndFalloffExponent . W ) ;
}
// Spot falloff
FVector L = ToLight . GetSafeNormal ( ) ;
Color * = FMath : : Square ( FMath : : Clamp ( ( ( L | Direction ) - SpotAngles . X ) * SpotAngles . Y , 0.0f , 1.0f ) ) ;
// Scale by visible area
Color / = PI * FMath : : Square ( SourceRadius ) ;
// Always opaque
Color . A = 1.0f ;
FViewElementPDI LightPDI ( & View , NULL ) ;
FMaterialRenderProxy * const ColoredMeshInstance = new ( FMemStack : : Get ( ) ) FColoredMaterialRenderProxy ( GEngine - > DebugMeshMaterial - > GetRenderProxy ( false ) , Color ) ;
DrawSphere ( & LightPDI , Origin , FVector ( SourceRadius , SourceRadius , SourceRadius ) , 8 , 6 , ColoredMeshInstance , SDPG_World ) ;
}
}
2014-03-14 14:13:41 -04:00
}
}
// Initialize the fog constants.
InitFogConstants ( ) ;
InitAtmosphereConstants ( ) ;
}
uint32 GetShadowQuality ( ) ;
/**
* Initialize scene ' s views .
* Check visibility , sort translucent items , etc .
*/
2014-06-27 11:07:13 -04:00
void FDeferredShadingSceneRenderer : : InitViews ( FRHICommandListImmediate & RHICmdList )
2014-03-14 14:13:41 -04:00
{
2014-10-20 10:43:43 -04:00
SCOPED_DRAW_EVENT ( RHICmdList , InitViews ) ;
2014-03-14 14:13:41 -04:00
SCOPE_CYCLE_COUNTER ( STAT_InitViewsTime ) ;
2014-08-28 13:54:31 -04:00
for ( int32 ViewIndex = 0 ; ViewIndex < Views . Num ( ) ; ViewIndex + + )
{
FViewInfo & View = Views [ ViewIndex ] ;
if ( ! GPostProcessing . AllowFullPostProcessing ( View , FeatureLevel ) )
{
// Disable anti-aliasing if we are not going to be able to apply final post process effects
View . FinalPostProcessSettings . AntiAliasingMethod = AAM_None ;
}
}
2014-06-27 11:07:13 -04:00
PreVisibilityFrameSetup ( RHICmdList ) ;
ComputeViewVisibility ( RHICmdList ) ;
2014-03-14 14:13:41 -04:00
PostVisibilityFrameSetup ( ) ;
FVector AverageViewPosition ( 0 ) ;
for ( int32 ViewIndex = 0 ; ViewIndex < Views . Num ( ) ; ViewIndex + + )
{
FViewInfo & View = Views [ ViewIndex ] ;
AverageViewPosition + = View . ViewMatrices . ViewOrigin / Views . Num ( ) ;
}
SortBasePassStaticData ( AverageViewPosition ) ;
bool bDynamicShadows = ViewFamily . EngineShowFlags . DynamicShadows & & GetShadowQuality ( ) > 0 ;
if ( bDynamicShadows & & ! IsSimpleDynamicLightingEnabled ( ) )
{
// Setup dynamic shadows.
2014-06-27 11:07:13 -04:00
InitDynamicShadows ( RHICmdList ) ;
2014-03-14 14:13:41 -04:00
}
2014-09-12 17:21:49 -04:00
// initialize per-view uniform buffer.
for ( int32 ViewIndex = 0 ; ViewIndex < Views . Num ( ) ; ViewIndex + + )
{
// Initialize the view's RHI resources.
Views [ ViewIndex ] . InitRHIResources ( nullptr ) ;
}
2014-03-14 14:13:41 -04:00
OnStartFrame ( ) ;
}
2015-01-22 11:05:14 -05:00
/*------------------------------------------------------------------------------
FLODSceneTree Implementation
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
void FLODSceneTree : : AddChildNode ( const FPrimitiveComponentId NodeId , FPrimitiveSceneInfo * ChildSceneInfo )
{
if ( NodeId . IsValid ( ) & & ChildSceneInfo )
{
FLODSceneNode * Node = SceneNodes . Find ( NodeId ) ;
if ( ! Node )
{
Node = & SceneNodes . Add ( NodeId , FLODSceneNode ( ) ) ;
// scene info can be added later depending on order of adding to the scene
// but at least add componentId, that way when parent is added, it will add its info properly
int32 ParentIndex = Scene - > PrimitiveComponentIds . Find ( NodeId ) ;
if ( Scene - > Primitives . IsValidIndex ( ParentIndex ) )
{
Node - > SceneInfo = Scene - > Primitives [ ParentIndex ] ;
}
}
Node - > AddChild ( ChildSceneInfo ) ;
}
}
void FLODSceneTree : : RemoveChildNode ( const FPrimitiveComponentId NodeId , FPrimitiveSceneInfo * ChildSceneInfo )
{
if ( NodeId . IsValid ( ) & & ChildSceneInfo )
{
FLODSceneNode * Node = SceneNodes . Find ( NodeId ) ;
if ( Node )
{
Node - > RemoveChild ( ChildSceneInfo ) ;
// delete from scene if no children remains
if ( Node - > ChildrenSceneInfos . Num ( ) = = 0 )
{
SceneNodes . Remove ( NodeId ) ;
}
}
}
}
void FLODSceneTree : : UpdateNodeSceneInfo ( FPrimitiveComponentId NodeId , FPrimitiveSceneInfo * SceneInfo )
{
FLODSceneNode * Node = SceneNodes . Find ( NodeId ) ;
if ( Node )
{
Node - > SceneInfo = SceneInfo ;
}
}
void FLODSceneTree : : PopulateHiddenFlagsToChildren ( FSceneBitArray & HiddenFlags , FLODSceneNode & Node )
{
// if already updated, no reason to do this
if ( Node . LatestUpdateCount ! = UpdateCount )
{
Node . LatestUpdateCount = UpdateCount ;
// if node doesn't have scene info, that means it doesn't to populate, children is disconnected, so don't bother
// in this case we still update children when this node is missing scene info
// because parent is showing, so you don't have to show anyway anybody below
// although this node might be MIA at this moment
for ( const auto & Child : Node . ChildrenSceneInfos )
{
const int32 ChildIndex = Child - > GetIndex ( ) ;
// first update the flags
FRelativeBitReference BitRef ( ChildIndex ) ;
HiddenFlags . AccessCorrespondingBit ( BitRef ) = true ;
// find the node for it
FLODSceneNode * ChildNode = SceneNodes . Find ( Child - > PrimitiveComponentId ) ;
// if you have child, populate it again,
if ( ChildNode )
{
PopulateHiddenFlagsToChildren ( HiddenFlags , * ChildNode ) ;
}
}
}
}
void FLODSceneTree : : PopulateHiddenFlags ( FViewInfo & View , FSceneBitArray & HiddenFlags )
{
+ + UpdateCount ;
// @todo this is experimental code - hide the children if parent is showing
for ( auto Iter = SceneNodes . CreateIterator ( ) ; Iter ; + + Iter )
{
FLODSceneNode & Node = Iter . Value ( ) ;
// if already updated, no reason to do this
if ( Node . LatestUpdateCount ! = UpdateCount )
{
Node . LatestUpdateCount = UpdateCount ;
// if node doesn't have scene info, that means it doesn't have any
if ( Node . SceneInfo )
{
int32 NodeIndex = Node . SceneInfo - > GetIndex ( ) ;
// if this node is visible, children shouldn't show up
if ( View . PrimitiveVisibilityMap [ NodeIndex ] )
{
for ( const auto & Child : Node . ChildrenSceneInfos )
{
const int32 ChildIndex = Child - > GetIndex ( ) ;
// first update the flags
FRelativeBitReference BitRef ( ChildIndex ) ;
HiddenFlags . AccessCorrespondingBit ( BitRef ) = true ;
// find the node for it
FLODSceneNode * ChildNode = SceneNodes . Find ( Child - > PrimitiveComponentId ) ;
// if you have child, populate it again,
if ( ChildNode )
{
PopulateHiddenFlagsToChildren ( HiddenFlags , * ChildNode ) ;
}
}
}
else
{
HiddenFlags . AccessCorrespondingBit ( FRelativeBitReference ( NodeIndex ) ) = true ;
}
}
}
}
}