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
# include "FunctionalTestingPrivatePCH.h"
# include "ObjectEditorUtils.h"
2014-10-27 08:35:45 -04:00
# include "VisualLogger/VisualLogger.h"
2015-03-11 11:02:39 -04:00
# if WITH_EDITORONLY_DATA
# include "FuncTestRenderingComponent.h"
# endif // WITH_EDITORONLY_DATA
# if WITH_EDITOR
# include "Engine/Selection.h"
# endif // WITH_EDITOR
2014-03-14 14:13:41 -04:00
2014-10-14 10:29:11 -04:00
AFunctionalTest : : AFunctionalTest ( const FObjectInitializer & ObjectInitializer )
: Super ( ObjectInitializer )
2014-03-14 14:13:41 -04:00
, TimesUpResult ( EFunctionalTestResult : : Failed )
, TimeLimit ( DefaultTimeLimit )
, TimesUpMessage ( NSLOCTEXT ( " FunctionalTest " , " DefaultTimesUpMessage " , " Time's up! " ) )
2014-04-23 19:29:53 -04:00
, bIsEnabled ( true )
2014-03-14 14:13:41 -04:00
, bIsRunning ( false )
2014-06-06 06:27:44 -04:00
, TotalTime ( 0.f )
2014-03-14 14:13:41 -04:00
{
PrimaryActorTick . bCanEverTick = true ;
PrimaryActorTick . bStartWithTickEnabled = false ;
2014-11-11 10:35:51 -05:00
2015-02-10 04:34:10 -05:00
SpriteComponent = CreateDefaultSubobject < UBillboardComponent > ( TEXT ( " Sprite " ) ) ;
2014-04-23 19:29:53 -04:00
if ( SpriteComponent )
{
SpriteComponent - > bHiddenInGame = false ;
2014-03-14 14:13:41 -04:00
# if WITH_EDITORONLY_DATA
2014-04-23 19:29:53 -04:00
if ( ! IsRunningCommandlet ( ) )
2014-04-02 18:09:23 -04:00
{
2014-04-23 19:29:53 -04:00
struct FConstructorStatics
2014-04-02 18:09:23 -04:00
{
2014-04-23 19:29:53 -04:00
ConstructorHelpers : : FObjectFinderOptional < UTexture2D > Texture ;
FName ID_FTests ;
FText NAME_FTests ;
2014-03-14 14:13:41 -04:00
2014-04-23 19:29:53 -04:00
FConstructorStatics ( )
: Texture ( TEXT ( " /Engine/EditorResources/S_FTest " ) )
, ID_FTests ( TEXT ( " FTests " ) )
, NAME_FTests ( NSLOCTEXT ( " SpriteCategory " , " FTests " , " FTests " ) )
{
}
} ;
static FConstructorStatics ConstructorStatics ;
2014-03-14 14:13:41 -04:00
2014-04-23 19:29:53 -04:00
SpriteComponent - > Sprite = ConstructorStatics . Texture . Get ( ) ;
SpriteComponent - > SpriteInfo . Category = ConstructorStatics . ID_FTests ;
SpriteComponent - > SpriteInfo . DisplayName = ConstructorStatics . NAME_FTests ;
2014-03-14 14:13:41 -04:00
}
2014-04-23 19:29:53 -04:00
# endif
RootComponent = SpriteComponent ;
2014-03-14 14:13:41 -04:00
}
2015-03-11 11:02:39 -04:00
# if WITH_EDITORONLY_DATA
RenderComp = CreateDefaultSubobject < UFuncTestRenderingComponent > ( TEXT ( " RenderComp " ) ) ;
RenderComp - > PostPhysicsComponentTick . bCanEverTick = false ;
RenderComp - > AttachParent = RootComponent ;
# endif // WITH_EDITORONLY_DATA
# if WITH_EDITOR
static bool bSelectionHandlerSetUp = false ;
if ( HasAnyFlags ( RF_ClassDefaultObject ) & & ! HasAnyFlags ( RF_TagGarbageTemp ) & & bSelectionHandlerSetUp = = false )
{
USelection : : SelectObjectEvent . AddStatic ( & AFunctionalTest : : OnSelectObject ) ;
bSelectionHandlerSetUp = true ;
}
# endif // WITH_EDITOR
2014-03-14 14:13:41 -04:00
}
void AFunctionalTest : : Tick ( float DeltaSeconds )
{
// already requested not to tick.
if ( bIsRunning = = false )
{
return ;
}
2015-10-30 17:41:13 -04:00
//Do not collect garbage during the test. We force GC at the end.
GetWorld ( ) - > DelayGarbageCollection ( ) ;
2014-06-06 06:27:44 -04:00
TotalTime + = DeltaSeconds ;
if ( TimeLimit > 0.f & & TotalTime > TimeLimit )
2014-03-14 14:13:41 -04:00
{
FinishTest ( TimesUpResult , TimesUpMessage . ToString ( ) ) ;
}
else
{
Super : : Tick ( DeltaSeconds ) ;
}
}
2014-06-17 08:31:02 -04:00
bool AFunctionalTest : : StartTest ( const TArray < FString > & Params )
2014-03-14 14:13:41 -04:00
{
2014-04-23 19:29:53 -04:00
FailureMessage = TEXT ( " " ) ;
2014-11-11 10:35:51 -05:00
2015-10-30 17:41:13 -04:00
//Do not collect garbage during the test. We force GC at the end.
GetWorld ( ) - > DelayGarbageCollection ( ) ;
2014-06-06 06:27:44 -04:00
TotalTime = 0.f ;
2014-03-14 14:13:41 -04:00
if ( TimeLimit > 0 )
{
SetActorTickEnabled ( true ) ;
}
bIsRunning = true ;
2014-06-09 11:13:04 -04:00
GoToObservationPoint ( ) ;
2014-03-14 14:13:41 -04:00
OnTestStart . Broadcast ( ) ;
2014-04-23 19:29:53 -04:00
return true ;
2014-03-14 14:13:41 -04:00
}
void AFunctionalTest : : FinishTest ( TEnumAsByte < EFunctionalTestResult : : Type > TestResult , const FString & Message )
{
2015-01-20 09:33:54 -05:00
const static UEnum * FTestResultTypeEnum = FindObject < UEnum > ( NULL , TEXT ( " FunctionalTesting.EFunctionalTestResult " ) ) ;
2014-03-14 14:13:41 -04:00
2014-06-09 11:13:04 -04:00
if ( bIsRunning = = false )
{
// ignore
return ;
}
2015-10-30 17:41:13 -04:00
//Force GC at the end of every test.
GetWorld ( ) - > ForceGarbageCollection ( ) ;
2014-06-17 08:31:02 -04:00
Result = TestResult ;
2014-03-14 14:13:41 -04:00
bIsRunning = false ;
SetActorTickEnabled ( false ) ;
OnTestFinished . Broadcast ( ) ;
2014-09-29 04:23:44 -04:00
AActor * * ActorToDestroy = AutoDestroyActors . GetData ( ) ;
2014-03-14 14:13:41 -04:00
for ( int32 ActorIndex = 0 ; ActorIndex < AutoDestroyActors . Num ( ) ; + + ActorIndex , + + ActorToDestroy )
{
if ( * ActorToDestroy ! = NULL )
{
// will be removed next frame
( * ActorToDestroy ) - > SetLifeSpan ( 0.01f ) ;
}
}
const FText ResultText = FTestResultTypeEnum - > GetEnumText ( TestResult . GetValue ( ) ) ;
2014-06-17 08:31:02 -04:00
const FString OutMessage = FString : : Printf ( TEXT ( " %s %s: \" %s \' " )
2015-10-06 15:59:09 -04:00
, * GetName ( )
2014-03-14 14:13:41 -04:00
, * ResultText . ToString ( )
, Message . IsEmpty ( ) = = false ? * Message : TEXT ( " Test finished " ) ) ;
2015-03-18 12:16:37 -04:00
const FString AdditionalDetails = FString : : Printf ( TEXT ( " %s %s, time %.2fs " ) , * GetAdditionalTestFinishedMessage ( TestResult ) , * OnAdditionalTestFinishedMessageRequest ( TestResult ) , TotalTime ) ;
2014-03-14 14:13:41 -04:00
AutoDestroyActors . Reset ( ) ;
2014-06-17 08:31:02 -04:00
2014-03-14 14:13:41 -04:00
switch ( TestResult . GetValue ( ) )
{
case EFunctionalTestResult : : Invalid :
case EFunctionalTestResult : : Error :
case EFunctionalTestResult : : Failed :
UE_VLOG ( this , LogFunctionalTest , Error , TEXT ( " %s " ) , * OutMessage ) ;
2015-10-06 15:59:09 -04:00
UE_LOG ( LogFunctionalTest , Error , TEXT ( " %s " ) , * OutMessage ) ;
2014-03-14 14:13:41 -04:00
break ;
2014-06-17 08:31:02 -04:00
case EFunctionalTestResult : : Running :
UE_VLOG ( this , LogFunctionalTest , Warning , TEXT ( " %s " ) , * OutMessage ) ;
2015-10-06 15:59:09 -04:00
UE_LOG ( LogFunctionalTest , Warning , TEXT ( " %s " ) , * OutMessage ) ;
2014-06-17 08:31:02 -04:00
break ;
2014-03-14 14:13:41 -04:00
default :
UE_VLOG ( this , LogFunctionalTest , Log , TEXT ( " %s " ) , * OutMessage ) ;
2015-10-06 15:59:09 -04:00
UE_LOG ( LogFunctionalTest , Log , TEXT ( " %s " ) , * OutMessage ) ;
2014-03-14 14:13:41 -04:00
break ;
}
2014-06-17 08:31:02 -04:00
if ( AdditionalDetails . IsEmpty ( ) = = false )
2014-06-16 09:55:19 -04:00
{
2015-10-06 15:59:09 -04:00
UE_LOG ( LogFunctionalTest , Log , TEXT ( " %s " ) , * AdditionalDetails ) ;
2014-06-16 09:55:19 -04:00
}
2014-03-14 14:13:41 -04:00
TestFinishedObserver . ExecuteIfBound ( this ) ;
}
2014-06-25 05:47:19 -04:00
void AFunctionalTest : : EndPlay ( const EEndPlayReason : : Type EndPlayReason )
{
TestFinishedObserver . Unbind ( ) ;
Super : : EndPlay ( EndPlayReason ) ;
}
2014-04-23 19:29:53 -04:00
void AFunctionalTest : : CleanUp ( )
{
FailureMessage = TEXT ( " " ) ;
}
2014-03-14 14:13:41 -04:00
//@todo add "warning" level here
void AFunctionalTest : : LogMessage ( const FString & Message )
{
2015-10-06 15:59:09 -04:00
UE_LOG ( LogFunctionalTest , Log , TEXT ( " %s " ) , * Message ) ;
2015-06-17 05:09:01 -04:00
UE_VLOG ( this , LogFunctionalTest , Log
2014-03-14 14:13:41 -04:00
, TEXT ( " %s> %s " )
2015-10-06 15:59:09 -04:00
, * GetName ( ) , * Message ) ;
2014-03-14 14:13:41 -04:00
}
void AFunctionalTest : : SetTimeLimit ( float InTimeLimit , TEnumAsByte < EFunctionalTestResult : : Type > InResult )
{
if ( InTimeLimit < 0.f )
{
UE_VLOG ( this , LogFunctionalTest , Warning
, TEXT ( " %s> Trying to set TimeLimit to less than 0. Falling back to 0 (infinite). " )
2015-10-06 15:59:09 -04:00
, * GetName ( ) ) ;
2014-03-14 14:13:41 -04:00
InTimeLimit = 0.f ;
}
TimeLimit = InTimeLimit ;
if ( InResult = = EFunctionalTestResult : : Invalid )
{
UE_VLOG ( this , LogFunctionalTest , Warning
, TEXT ( " %s> Trying to set test Result to \' Invalid \' . Falling back to \' Failed \' " )
2015-10-06 15:59:09 -04:00
, * GetName ( ) ) ;
2014-03-14 14:13:41 -04:00
2014-05-12 08:42:24 -04:00
InResult = EFunctionalTestResult : : Failed ;
2014-03-14 14:13:41 -04:00
}
TimesUpResult = InResult ;
}
2015-03-11 11:02:39 -04:00
void AFunctionalTest : : GatherRelevantActors ( TArray < AActor * > & OutActors ) const
{
if ( ObservationPoint )
{
OutActors . AddUnique ( ObservationPoint ) ;
}
for ( auto Actor : AutoDestroyActors )
{
if ( Actor )
{
OutActors . AddUnique ( Actor ) ;
}
}
OutActors . Append ( DebugGatherRelevantActors ( ) ) ;
}
2015-10-30 17:41:13 -04:00
void AFunctionalTest : : AddRerun ( FName Reason )
{
RerunCauses . Add ( Reason ) ;
}
FName AFunctionalTest : : GetCurrentRerunReason ( ) const
{
return CurrentRerunCause ;
}
2014-03-14 14:13:41 -04:00
void AFunctionalTest : : RegisterAutoDestroyActor ( AActor * ActorToAutoDestroy )
{
AutoDestroyActors . AddUnique ( ActorToAutoDestroy ) ;
}
# if WITH_EDITOR
void AFunctionalTest : : PostEditChangeProperty ( struct FPropertyChangedEvent & PropertyChangedEvent )
{
static const FName NAME_FunctionalTesting = FName ( TEXT ( " FunctionalTesting " ) ) ;
static const FName NAME_TimeLimit = FName ( TEXT ( " TimeLimit " ) ) ;
static const FName NAME_TimesUpResult = FName ( TEXT ( " TimesUpResult " ) ) ;
Super : : PostEditChangeProperty ( PropertyChangedEvent ) ;
if ( PropertyChangedEvent . Property ! = NULL )
{
if ( FObjectEditorUtils : : GetCategoryFName ( PropertyChangedEvent . Property ) = = NAME_FunctionalTesting )
{
// first validate new data since there are some dependencies
if ( PropertyChangedEvent . Property - > GetFName ( ) = = NAME_TimeLimit )
{
if ( TimeLimit < 0.f )
{
TimeLimit = 0.f ;
}
}
else if ( PropertyChangedEvent . Property - > GetFName ( ) = = NAME_TimesUpResult )
{
if ( TimesUpResult = = EFunctionalTestResult : : Invalid )
{
TimesUpResult = EFunctionalTestResult : : Failed ;
}
}
}
}
}
2015-03-11 11:02:39 -04:00
void AFunctionalTest : : OnSelectObject ( UObject * NewSelection )
{
AFunctionalTest * AsFTest = Cast < AFunctionalTest > ( NewSelection ) ;
if ( AsFTest )
{
AsFTest - > MarkComponentsRenderStateDirty ( ) ;
}
}
2014-04-23 19:29:53 -04:00
# endif // WITH_EDITOR
2014-06-09 11:13:04 -04:00
void AFunctionalTest : : GoToObservationPoint ( )
{
if ( ObservationPoint = = NULL )
{
return ;
}
UWorld * World = GetWorld ( ) ;
2015-09-11 14:49:47 -04:00
if ( World & & World - > GetGameInstance ( ) )
2014-06-09 11:13:04 -04:00
{
2015-09-11 14:49:47 -04:00
APlayerController * PC = World - > GetGameInstance ( ) - > GetFirstLocalPlayerController ( ) ;
2014-06-09 11:13:04 -04:00
if ( PC & & PC - > GetPawn ( ) )
{
2014-06-26 17:26:58 -04:00
PC - > GetPawn ( ) - > TeleportTo ( ObservationPoint - > GetActorLocation ( ) , ObservationPoint - > GetActorRotation ( ) , /*bIsATest=*/ false , /*bNoCheck=*/ true ) ;
2014-06-09 11:13:04 -04:00
PC - > SetControlRotation ( ObservationPoint - > GetActorRotation ( ) ) ;
}
}
2014-10-30 17:10:56 -04:00
}
/** Returns SpriteComponent subobject **/
UBillboardComponent * AFunctionalTest : : GetSpriteComponent ( ) { return SpriteComponent ; }
2015-10-28 08:58:16 -04:00
//////////////////////////////////////////////////////////////////////////
FPerfStatsRecord : : FPerfStatsRecord ( FString InName )
: Name ( InName )
2015-11-12 12:38:50 -05:00
, GPUBudget ( 0.0f )
, RenderThreadBudget ( 0.0f )
, GameThreadBudget ( 0.0f )
2015-10-28 08:58:16 -04:00
{
}
2015-11-12 12:38:50 -05:00
void FPerfStatsRecord : : SetBudgets ( float InGPUBudget , float InRenderThreadBudget , float InGameThreadBudget )
{
GPUBudget = InGPUBudget ;
RenderThreadBudget = InRenderThreadBudget ;
GameThreadBudget = InGameThreadBudget ;
}
2015-10-28 08:58:16 -04:00
FString FPerfStatsRecord : : GetReportString ( ) const
{
2015-10-30 17:41:13 -04:00
return FString : : Printf ( TEXT ( " %s,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f " ) ,
2015-10-28 08:58:16 -04:00
* Name ,
2015-10-30 17:41:13 -04:00
Record . FrameTimeTracker . GetMinValue ( ) - Baseline . FrameTimeTracker . GetMinValue ( ) ,
Record . FrameTimeTracker . GetAvgValue ( ) - Baseline . FrameTimeTracker . GetAvgValue ( ) ,
Record . FrameTimeTracker . GetMaxValue ( ) - Baseline . FrameTimeTracker . GetMaxValue ( ) ,
Record . RenderThreadTimeTracker . GetMinValue ( ) - Baseline . RenderThreadTimeTracker . GetMinValue ( ) ,
Record . RenderThreadTimeTracker . GetAvgValue ( ) - Baseline . RenderThreadTimeTracker . GetAvgValue ( ) ,
Record . RenderThreadTimeTracker . GetMaxValue ( ) - Baseline . RenderThreadTimeTracker . GetMaxValue ( ) ,
Record . GameThreadTimeTracker . GetMinValue ( ) - Baseline . GameThreadTimeTracker . GetMinValue ( ) ,
Record . GameThreadTimeTracker . GetAvgValue ( ) - Baseline . GameThreadTimeTracker . GetAvgValue ( ) ,
Record . GameThreadTimeTracker . GetMaxValue ( ) - Baseline . GameThreadTimeTracker . GetMaxValue ( ) ,
Record . GPUTimeTracker . GetMinValue ( ) - Baseline . GPUTimeTracker . GetMinValue ( ) ,
Record . GPUTimeTracker . GetAvgValue ( ) - Baseline . GPUTimeTracker . GetAvgValue ( ) ,
Record . GPUTimeTracker . GetMaxValue ( ) - Baseline . GPUTimeTracker . GetMaxValue ( ) ) ;
2015-10-28 08:58:16 -04:00
}
2015-10-30 17:41:13 -04:00
FString FPerfStatsRecord : : GetBaselineString ( ) const
2015-10-28 08:58:16 -04:00
{
2015-10-30 17:41:13 -04:00
return FString : : Printf ( TEXT ( " %s,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f " ) ,
* Name ,
Baseline . FrameTimeTracker . GetMinValue ( ) ,
Baseline . FrameTimeTracker . GetAvgValue ( ) ,
Baseline . FrameTimeTracker . GetMaxValue ( ) ,
Baseline . RenderThreadTimeTracker . GetMinValue ( ) ,
Baseline . RenderThreadTimeTracker . GetAvgValue ( ) ,
Baseline . RenderThreadTimeTracker . GetMaxValue ( ) ,
Baseline . GameThreadTimeTracker . GetMinValue ( ) ,
Baseline . GameThreadTimeTracker . GetAvgValue ( ) ,
Baseline . GameThreadTimeTracker . GetMaxValue ( ) ,
Baseline . GPUTimeTracker . GetMinValue ( ) ,
Baseline . GPUTimeTracker . GetAvgValue ( ) ,
Baseline . GPUTimeTracker . GetMaxValue ( ) ) ;
}
2015-10-28 08:58:16 -04:00
2015-10-30 17:41:13 -04:00
FString FPerfStatsRecord : : GetRecordString ( ) const
{
return FString : : Printf ( TEXT ( " %s,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f " ) ,
* Name ,
Record . FrameTimeTracker . GetMinValue ( ) ,
Record . FrameTimeTracker . GetAvgValue ( ) ,
Record . FrameTimeTracker . GetMaxValue ( ) ,
Record . RenderThreadTimeTracker . GetMinValue ( ) ,
Record . RenderThreadTimeTracker . GetAvgValue ( ) ,
Record . RenderThreadTimeTracker . GetMaxValue ( ) ,
Record . GameThreadTimeTracker . GetMinValue ( ) ,
Record . GameThreadTimeTracker . GetAvgValue ( ) ,
Record . GameThreadTimeTracker . GetMaxValue ( ) ,
Record . GPUTimeTracker . GetMinValue ( ) ,
Record . GPUTimeTracker . GetAvgValue ( ) ,
Record . GPUTimeTracker . GetMaxValue ( ) ) ;
}
2015-11-12 12:38:50 -05:00
FString FPerfStatsRecord : : GetOverBudgetString ( ) const
{
double Min , Max , Avg ;
GetRenderThreadTimes ( Min , Max , Avg ) ;
float RTBudgetFrac = Max / RenderThreadBudget ;
GetGameThreadTimes ( Min , Max , Avg ) ;
float GTBudgetFrac = Max / GameThreadBudget ;
GetGPUTimes ( Min , Max , Avg ) ;
float GPUBudgetFrac = Max / GPUBudget ;
return FString : : Printf ( TEXT ( " %s,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f " ) ,
* Name ,
Record . RenderThreadTimeTracker . GetMaxValue ( ) ,
RenderThreadBudget ,
RTBudgetFrac ,
Record . GameThreadTimeTracker . GetMaxValue ( ) ,
GameThreadBudget ,
GTBudgetFrac ,
Record . GPUTimeTracker . GetMaxValue ( ) ,
GPUBudget ,
GPUBudgetFrac
) ;
}
bool FPerfStatsRecord : : IsWithinGPUBudget ( ) const
{
double Min , Max , Avg ;
GetGPUTimes ( Min , Max , Avg ) ;
return Max < = GPUBudget ;
}
bool FPerfStatsRecord : : IsWithinGameThreadBudget ( ) const
{
double Min , Max , Avg ;
GetGameThreadTimes ( Min , Max , Avg ) ;
return Max < = GameThreadBudget ;
}
bool FPerfStatsRecord : : IsWithinRenderThreadBudget ( ) const
{
double Min , Max , Avg ;
GetRenderThreadTimes ( Min , Max , Avg ) ;
return Max < = RenderThreadBudget ;
}
2015-10-30 17:41:13 -04:00
void FPerfStatsRecord : : GetGPUTimes ( double & OutMin , double & OutMax , double & OutAvg ) const
{
OutMin = Record . GPUTimeTracker . GetMinValue ( ) - Baseline . GPUTimeTracker . GetMinValue ( ) ;
OutMax = Record . GPUTimeTracker . GetMaxValue ( ) - Baseline . GPUTimeTracker . GetMaxValue ( ) ;
OutAvg = Record . GPUTimeTracker . GetAvgValue ( ) - Baseline . GPUTimeTracker . GetAvgValue ( ) ;
}
void FPerfStatsRecord : : GetGameThreadTimes ( double & OutMin , double & OutMax , double & OutAvg ) const
{
OutMin = Record . GameThreadTimeTracker . GetMinValue ( ) - Baseline . GameThreadTimeTracker . GetMinValue ( ) ;
OutMax = Record . GameThreadTimeTracker . GetMaxValue ( ) - Baseline . GameThreadTimeTracker . GetMaxValue ( ) ;
OutAvg = Record . GameThreadTimeTracker . GetAvgValue ( ) - Baseline . GameThreadTimeTracker . GetAvgValue ( ) ;
}
void FPerfStatsRecord : : GetRenderThreadTimes ( double & OutMin , double & OutMax , double & OutAvg ) const
{
OutMin = Record . RenderThreadTimeTracker . GetMinValue ( ) - Baseline . RenderThreadTimeTracker . GetMinValue ( ) ;
OutMax = Record . RenderThreadTimeTracker . GetMaxValue ( ) - Baseline . RenderThreadTimeTracker . GetMaxValue ( ) ;
OutAvg = Record . RenderThreadTimeTracker . GetAvgValue ( ) - Baseline . RenderThreadTimeTracker . GetAvgValue ( ) ;
}
void FPerfStatsRecord : : Sample ( UWorld * World , float DeltaSeconds , bool bBaseline )
{
check ( World ) ;
const FStatUnitData * StatUnitData = World - > GetGameViewport ( ) - > GetStatUnitData ( ) ;
2015-10-28 08:58:16 -04:00
check ( StatUnitData ) ;
2015-10-30 17:41:13 -04:00
if ( bBaseline )
{
Baseline . FrameTimeTracker . AddSample ( StatUnitData - > RawFrameTime ) ;
Baseline . GameThreadTimeTracker . AddSample ( FPlatformTime : : ToMilliseconds ( GGameThreadTime ) ) ;
Baseline . RenderThreadTimeTracker . AddSample ( FPlatformTime : : ToMilliseconds ( GRenderThreadTime ) ) ;
Baseline . GPUTimeTracker . AddSample ( FPlatformTime : : ToMilliseconds ( GGPUFrameTime ) ) ;
Baseline . NumFrames + + ;
Baseline . SumTimeSeconds + = DeltaSeconds ;
}
else
{
Record . FrameTimeTracker . AddSample ( StatUnitData - > RawFrameTime ) ;
Record . GameThreadTimeTracker . AddSample ( FPlatformTime : : ToMilliseconds ( GGameThreadTime ) ) ;
Record . RenderThreadTimeTracker . AddSample ( FPlatformTime : : ToMilliseconds ( GRenderThreadTime ) ) ;
Record . GPUTimeTracker . AddSample ( FPlatformTime : : ToMilliseconds ( GGPUFrameTime ) ) ;
Record . NumFrames + + ;
Record . SumTimeSeconds + = DeltaSeconds ;
}
2015-10-28 08:58:16 -04:00
}
2015-10-30 17:41:13 -04:00
UAutomationPerformaceHelper : : UAutomationPerformaceHelper ( )
: bRecordingBasicStats ( false )
, bRecordingBaselineBasicStats ( false )
, bRecordingCPUCapture ( false )
, bRecordingStatsFile ( false )
2015-10-28 08:58:16 -04:00
{
}
2015-10-30 17:41:13 -04:00
void UAutomationPerformaceHelper : : BeginRecordingBaseline ( FString RecordName )
2015-10-28 08:58:16 -04:00
{
2015-10-30 17:41:13 -04:00
bRecordingBasicStats = true ;
bRecordingBaselineBasicStats = true ;
2015-10-28 08:58:16 -04:00
Records . Add ( FPerfStatsRecord ( RecordName ) ) ;
2015-10-30 17:41:13 -04:00
GEngine - > SetEngineStat ( GetOuter ( ) - > GetWorld ( ) , GetOuter ( ) - > GetWorld ( ) - > GetGameViewport ( ) , TEXT ( " Unit " ) , true ) ;
2015-10-28 08:58:16 -04:00
}
2015-10-30 17:41:13 -04:00
void UAutomationPerformaceHelper : : EndRecordingBaseline ( )
{
bRecordingBaselineBasicStats = false ;
bRecordingBasicStats = false ;
}
2015-11-12 12:38:50 -05:00
void UAutomationPerformaceHelper : : BeginRecording ( FString RecordName , float InGPUBudget , float InRenderThreadBudget , float InGameThreadBudget )
2015-10-30 17:41:13 -04:00
{
//Ensure we're recording engine stats.
GEngine - > SetEngineStat ( GetOuter ( ) - > GetWorld ( ) , GetOuter ( ) - > GetWorld ( ) - > GetGameViewport ( ) , TEXT ( " Unit " ) , true ) ;
bRecordingBasicStats = true ;
bRecordingBaselineBasicStats = false ;
2015-11-12 12:38:50 -05:00
FPerfStatsRecord * CurrRecord = GetCurrentRecord ( ) ;
if ( ! CurrRecord | | CurrRecord - > Name ! = RecordName )
2015-10-30 17:41:13 -04:00
{
Records . Add ( FPerfStatsRecord ( RecordName ) ) ;
2015-11-12 12:38:50 -05:00
CurrRecord = GetCurrentRecord ( ) ;
2015-10-30 17:41:13 -04:00
}
2015-11-12 12:38:50 -05:00
check ( CurrRecord ) ;
CurrRecord - > SetBudgets ( InGPUBudget , InRenderThreadBudget , InGameThreadBudget ) ;
2015-10-30 17:41:13 -04:00
}
void UAutomationPerformaceHelper : : EndRecording ( )
2015-10-28 08:58:16 -04:00
{
if ( const FPerfStatsRecord * Record = GetCurrentRecord ( ) )
{
UE_LOG ( LogFunctionalTest , Log , TEXT ( " Finished Perf Stats Record: \n %s " ) , * Record - > GetReportString ( ) ) ;
}
2015-10-30 17:41:13 -04:00
bRecordingBasicStats = false ;
2015-10-28 08:58:16 -04:00
}
2015-10-30 17:41:13 -04:00
void UAutomationPerformaceHelper : : Tick ( float DeltaSeconds )
{
if ( bRecordingBasicStats )
{
Sample ( DeltaSeconds ) ;
}
//Other stats need ticking?
}
void UAutomationPerformaceHelper : : Sample ( float DeltaSeconds )
2015-10-28 08:58:16 -04:00
{
int32 Index = Records . Num ( ) - 1 ;
2015-10-30 17:41:13 -04:00
if ( Index > = 0 & & bRecordingBasicStats )
2015-10-28 08:58:16 -04:00
{
2015-10-30 17:41:13 -04:00
Records [ Index ] . Sample ( GetOuter ( ) - > GetWorld ( ) , DeltaSeconds , bRecordingBaselineBasicStats ) ;
2015-10-28 08:58:16 -04:00
}
}
2015-10-30 17:41:13 -04:00
void UAutomationPerformaceHelper : : WriteLogFile ( const FString & CaptureDir , const FString & CaptureExtension )
2015-10-28 08:58:16 -04:00
{
FString PathName = FPaths : : ProfilingDir ( ) ;
if ( ! CaptureDir . IsEmpty ( ) )
{
PathName = PathName + ( CaptureDir + TEXT ( " / " ) ) ;
IFileManager : : Get ( ) . MakeDirectory ( * PathName ) ;
}
FString Extension = CaptureExtension ;
if ( Extension . IsEmpty ( ) )
{
Extension = TEXT ( " perf.csv " ) ;
}
2015-10-30 17:41:13 -04:00
const FString Filename = OutputFileBase + Extension ;
2015-10-28 08:58:16 -04:00
const FString FilenameFull = PathName + Filename ;
2015-11-12 12:38:50 -05:00
const FString OverBudgetTableHeader = TEXT ( " TestName, MaxRT, RT Budget, RT Frac, MaxGT, GT Budget, GT Frac, MaxGPU, GPU Budget, GPU Frac \n " ) ;
FString OverbudgetTable ;
const FString DataTableHeader = TEXT ( " TestName,MinFrameTime,AvgFrameTime,MaxFrameTime,MinRT,AvgRT,MaxRT,MinGT,AvgGT,MaxGT,MinGPU,AvgGPU,MaxGPU \n " ) ;
2015-10-30 17:41:13 -04:00
FString AdjustedTable ;
FString RecordTable ;
FString BaselineTable ;
for ( FPerfStatsRecord & Record : Records )
{
2015-11-12 12:38:50 -05:00
AdjustedTable + = Record . GetReportString ( ) + FString ( TEXT ( " \n " ) ) ;
RecordTable + = Record . GetRecordString ( ) + FString ( TEXT ( " \n " ) ) ;
2015-10-30 17:41:13 -04:00
BaselineTable + = Record . GetBaselineString ( ) + FString ( TEXT ( " \n " ) ) ;
2015-11-12 12:38:50 -05:00
if ( ! Record . IsWithinGPUBudget ( ) | | ! Record . IsWithinRenderThreadBudget ( ) | | ! Record . IsWithinGameThreadBudget ( ) )
{
OverbudgetTable + = Record . GetOverBudgetString ( ) + FString ( TEXT ( " \n " ) ) ;
}
2015-10-30 17:41:13 -04:00
}
2015-11-12 12:38:50 -05:00
FString FileContents = FString : : Printf ( TEXT ( " Over Budget Tests \n %s%s \n Adjusted Results \n %s%s \n Raw Results \n %s%s \n Baseline Results \n %s%s \n " ) ,
* OverBudgetTableHeader , * OverbudgetTable , * DataTableHeader , * AdjustedTable , * DataTableHeader , * RecordTable , * DataTableHeader , * BaselineTable ) ;
2015-10-28 08:58:16 -04:00
FFileHelper : : SaveStringToFile ( FileContents , * FilenameFull ) ;
UE_LOG ( LogTemp , Display , TEXT ( " Finished test, wrote file to %s " ) , * FilenameFull ) ;
Records . Empty ( ) ;
2015-10-30 17:41:13 -04:00
bRecordingBasicStats = false ;
bRecordingBaselineBasicStats = false ;
2015-10-28 08:58:16 -04:00
}
2015-10-30 17:41:13 -04:00
bool UAutomationPerformaceHelper : : IsRecording ( ) const
{
return bRecordingBasicStats ;
}
void UAutomationPerformaceHelper : : OnBeginTests ( )
{
OutputFileBase = CreateProfileFilename ( TEXT ( " " ) , true ) ;
StartOfTestingTime = FDateTime : : Now ( ) . ToString ( ) ;
}
void UAutomationPerformaceHelper : : OnAllTestsComplete ( )
{
if ( bRecordingBaselineBasicStats )
{
EndRecordingBaseline ( ) ;
}
if ( bRecordingBasicStats )
{
EndRecording ( ) ;
}
if ( bRecordingCPUCapture )
{
StopCPUProfiling ( ) ;
}
if ( bRecordingStatsFile )
{
EndStatsFile ( ) ;
}
if ( Records . Num ( ) > 0 )
{
WriteLogFile ( TEXT ( " " ) , TEXT ( " perf.csv " ) ) ;
}
}
2015-11-12 12:38:50 -05:00
bool UAutomationPerformaceHelper : : IsCurrentRecordWithinGPUBudget ( ) const
2015-10-30 17:41:13 -04:00
{
if ( const FPerfStatsRecord * Curr = GetCurrentRecord ( ) )
{
2015-11-12 12:38:50 -05:00
return Curr - > IsWithinGPUBudget ( ) ;
2015-10-30 17:41:13 -04:00
}
2015-11-12 12:38:50 -05:00
return true ;
2015-10-30 17:41:13 -04:00
}
2015-11-12 12:38:50 -05:00
bool UAutomationPerformaceHelper : : IsCurrentRecordWithinGameThreadBudget ( ) const
2015-10-30 17:41:13 -04:00
{
if ( const FPerfStatsRecord * Curr = GetCurrentRecord ( ) )
{
2015-11-12 12:38:50 -05:00
return Curr - > IsWithinGameThreadBudget ( ) ;
2015-10-30 17:41:13 -04:00
}
2015-11-12 12:38:50 -05:00
return true ;
2015-10-30 17:41:13 -04:00
}
2015-11-12 12:38:50 -05:00
bool UAutomationPerformaceHelper : : IsCurrentRecordWithinRenderThreadBudget ( ) const
2015-10-30 17:41:13 -04:00
{
if ( const FPerfStatsRecord * Curr = GetCurrentRecord ( ) )
{
2015-11-12 12:38:50 -05:00
return Curr - > IsWithinRenderThreadBudget ( ) ;
2015-10-30 17:41:13 -04:00
}
2015-11-12 12:38:50 -05:00
return true ;
2015-10-30 17:41:13 -04:00
}
const FPerfStatsRecord * UAutomationPerformaceHelper : : GetCurrentRecord ( ) const
2015-10-28 08:58:16 -04:00
{
int32 Index = Records . Num ( ) - 1 ;
2015-10-30 17:41:13 -04:00
if ( Index > = 0 )
2015-10-28 08:58:16 -04:00
{
return & Records [ Index ] ;
}
return nullptr ;
2015-10-30 17:41:13 -04:00
}
2015-11-12 12:38:50 -05:00
FPerfStatsRecord * UAutomationPerformaceHelper : : GetCurrentRecord ( )
{
int32 Index = Records . Num ( ) - 1 ;
if ( Index > = 0 )
{
return & Records [ Index ] ;
}
return nullptr ;
}
2015-10-30 17:41:13 -04:00
void UAutomationPerformaceHelper : : StartCPUProfiling ( )
{
UE_LOG ( LogFunctionalTest , Log , TEXT ( " START PROFILING... " ) ) ;
ExternalProfiler . StartProfiler ( false ) ;
}
void UAutomationPerformaceHelper : : StopCPUProfiling ( )
{
UE_LOG ( LogFunctionalTest , Log , TEXT ( " STOP PROFILING... " ) ) ;
ExternalProfiler . StopProfiler ( ) ;
}
void UAutomationPerformaceHelper : : TriggerGPUTrace ( )
{
// Need to look at Razor GPU to work out what can be done here.
}
void UAutomationPerformaceHelper : : BeginStatsFile ( const FString & RecordName )
{
FString MapName = GetOuter ( ) - > GetWorld ( ) - > GetMapName ( ) ;
FString Cmd = FString : : Printf ( TEXT ( " Stat StartFile %s-%s/%s.ue4stats " ) , * MapName , * StartOfTestingTime , * RecordName ) ;
GEngine - > Exec ( GetOuter ( ) - > GetWorld ( ) , * Cmd ) ;
}
void UAutomationPerformaceHelper : : EndStatsFile ( )
{
GEngine - > Exec ( GetOuter ( ) - > GetWorld ( ) , TEXT ( " Stat StopFile " ) ) ;
2015-10-28 08:58:16 -04:00
}