// Copyright Epic Games, Inc. All Rights Reserved. #include "ProfilerDataSource.h" #if STATS #include "Containers/MapBuilder.h" #include "ProfilerStream.h" #include "ProfilerDataProvider.h" #include "ProfilerSession.h" #define LOCTEXT_NAMESPACE "ProfilerDataSource" /*----------------------------------------------------------------------------- FGraphDataSource -----------------------------------------------------------------------------*/ const int32 FTimeAccuracy::_FPS008 = 8; const int32 FTimeAccuracy::_FPS015 = 15; const int32 FTimeAccuracy::_FPS030 = 30; const int32 FTimeAccuracy::_FPS060 = 60; const int32 FTimeAccuracy::_FPS120 = 120; /*----------------------------------------------------------------------------- FGraphDataSource -----------------------------------------------------------------------------*/ FGraphDataSource::FGraphDataSource( const TSharedRef& InProfilerSession, const uint32 InStatID ) : FGraphDataSourceDescription( InStatID ) , ThisCachedDataByIndex() , ThisCachedDataByTime( FTimeAccuracy::FPS060 ) , ProfilerSession( InProfilerSession ) { const TSharedRef MetaData = ProfilerSession->GetMetaData(); const FProfilerStat& Stat = MetaData->GetStatByID(InStatID); const FProfilerGroup& Group = Stat.OwningGroup(); Initialize( Stat.Name().GetPlainNameString(), Group.Name().GetPlainNameString(), Stat.Type(), ProfilerSession->GetCreationTime() ); switch( GetSampleType() ) { case EProfilerSampleTypes::Memory: { // By default we show memory data as KBs. Scale = 1.0f / 1024.0f; break; } default: { Scale = 1.0f; } } } const TGraphDataType FGraphDataSource::GetUncachedValueFromIndex( const uint32 FrameIndex ) const { check( FrameIndex < ProfilerSession->GetDataProvider()->GetNumFrames() ); double Result = 0.0; // Hierarchical samples are stored in different location. // We skip hierarchical samples to ignore misleading recursion which would be counted twice etc. if (GetSampleType() == EProfilerSampleTypes::HierarchicalTime) { const TMap& InclusiveAggregates = ProfilerSession->GetInclusiveAggregateStackStats( FrameIndex ); const FInclusiveTime* InclusiveTime = InclusiveAggregates.Find( GetStatID() ); if (InclusiveTime) { Result = ProfilerSession->GetMetaData()->ConvertCyclesToMS( InclusiveTime->DurationCycles ) * Scale; } } else { const TSharedRef DataProvider = ProfilerSession->GetDataProvider(); const FIntPoint& IndicesForFrame = DataProvider->GetSamplesIndicesForFrame( FrameIndex ); const uint32 SampleStartIndex = IndicesForFrame.X; const uint32 SampleEndIndex = IndicesForFrame.Y; const FProfilerSampleArray& Collection = DataProvider->GetCollection(); for (uint32 SampleIndex = SampleStartIndex; SampleIndex < SampleEndIndex; SampleIndex++) { const FProfilerSample& ProfilerSample = Collection[SampleIndex]; const bool bValidStat = ProfilerSample.StatID() == GetStatID(); if (bValidStat) { Result += ProfilerSample.GetDoubleValue() * Scale; } } } return (TGraphDataType)Result; } const TGraphDataType FGraphDataSource::GetUncachedValueFromTimeRange( const float StartTimeMS, const float EndTimeMS ) const { const TSharedRef DataProvider = ProfilerSession->GetDataProvider(); const FIntPoint IndicesForFrame = DataProvider->GetClosestSamplesIndicesForTime( StartTimeMS, EndTimeMS ); const uint32 StartFrameIndex = IndicesForFrame.X; const uint32 EndFrameIndex = IndicesForFrame.Y; TGraphDataType ResultMax = (TGraphDataType)MIN_int32; // Iterate through all frames and calculate the maximum value. for( uint32 FrameIndex = StartFrameIndex; FrameIndex < EndFrameIndex; ++FrameIndex ) { const TGraphDataType DataSourceValue = const_cast(this)->GetValueFromIndex( FrameIndex ); ResultMax = FMath::Max( ResultMax, DataSourceValue ); } return (TGraphDataType)ResultMax; } const uint32 FGraphDataSource::GetNumFrames() const { return ProfilerSession->GetDataProvider()->GetNumFrames(); } const float FGraphDataSource::GetTotalTimeMS() const { return static_cast(ProfilerSession->GetDataProvider()->GetTotalTimeMS()); } const TSharedRef FGraphDataSource::GetDataProvider() const { return ProfilerSession->GetDataProvider(); } const FProfilerAggregatedStat* FGraphDataSource::GetAggregatedStat() const { return ProfilerSession->GetAggregatedStat( StatID ); } const FGuid FGraphDataSource::GetSessionInstanceID() const { return ProfilerSession->GetInstanceID(); } /*----------------------------------------------------------------------------- FCombinedGraphDataSource -----------------------------------------------------------------------------*/ FCombinedGraphDataSource::FCombinedGraphDataSource( const uint32 InStatID, const FTimeAccuracy::Type InTimeAccuracy ) : FGraphDataSourceDescription( InStatID ) , ThisCachedDataByTime( InTimeAccuracy ) { } const FVector FCombinedGraphDataSource::GetUncachedValueFromTimeRange( const float StartTimeMS, const float EndTimeMS ) const { // X=Min, Y=Max, Z=Avg FVector3f AggregatedValue( (TGraphDataType)MAX_int32, (TGraphDataType)MIN_int32, 0.0f ); const uint32 NumSources = GraphDataSources.Num(); const float InvNumSources = 1.0f / (float)NumSources; for( auto It = GetSourcesIterator(); It; ++It ) { const TSharedRef& GraphDataSource = It.Value(); const float DataSourceValue = GraphDataSource->GetValueFromTimeRange( StartTimeMS, EndTimeMS ); AggregatedValue.X = FMath::Min( AggregatedValue.X, DataSourceValue ); AggregatedValue.Y = FMath::Max( AggregatedValue.Y, DataSourceValue ); AggregatedValue.Z += DataSourceValue; } AggregatedValue.Z *= InvNumSources; return (FVector)AggregatedValue; } void FCombinedGraphDataSource::GetStartIndicesFromTimeRange( const float StartTimeMS, const float EndTimeMS, TMap& out_StartIndices ) const { for( auto It = GetSourcesIterator(); It; ++It ) { const TSharedRef& GraphDataSource = It.Value(); const FIntPoint IndicesForFrame = GraphDataSource->GetDataProvider()->GetClosestSamplesIndicesForTime( StartTimeMS, EndTimeMS ); const uint32 StartFrameIndex = IndicesForFrame.X; const uint32 EndFrameIndex = IndicesForFrame.Y; uint32 FrameIndex = 0; float MaxFrameTime = 0.0f; // Iterate through all frames and find the highest frame time. for( uint32 InnerFrameIndex = StartFrameIndex; InnerFrameIndex < EndFrameIndex; ++InnerFrameIndex ) { const float InnerFrameTime = GraphDataSource->GetDataProvider()->GetFrameTimeMS( InnerFrameIndex ); if( InnerFrameTime > MaxFrameTime ) { MaxFrameTime = InnerFrameTime; FrameIndex = InnerFrameIndex; } } if( MaxFrameTime > 0.0f ) { out_StartIndices.Add( GraphDataSource->GetSessionInstanceID(), FrameIndex ); } } } /*----------------------------------------------------------------------------- FEventGraphData -----------------------------------------------------------------------------*/ //namespace NEventGraphSample { FName FEventGraphConsts::RootEvent = TEXT("RootEvent"); FName FEventGraphConsts::Self = TEXT("Self"); FName FEventGraphConsts::FakeRoot = TEXT("FakeRoot"); /*----------------------------------------------------------------------------- FEventGraphSample -----------------------------------------------------------------------------*/ FEventProperty FEventGraphSample::Properties[ (uint32)EEventPropertyIndex::InvalidOrMax ] = { // Properties FEventProperty( EEventPropertyIndex::StatName, TEXT( "StatName" ), STRUCT_OFFSET( FEventGraphSample, _StatName ), EEventPropertyFormatters::Name ), FEventProperty( EEventPropertyIndex::InclusiveTimeMS, TEXT( "InclusiveTimeMS" ), STRUCT_OFFSET( FEventGraphSample, _InclusiveTimeMS ), EEventPropertyFormatters::TimeMS ), FEventProperty( EEventPropertyIndex::InclusiveTimePct, TEXT( "InclusiveTimePct" ), STRUCT_OFFSET( FEventGraphSample, _InclusiveTimePct ), EEventPropertyFormatters::TimePct ), FEventProperty( EEventPropertyIndex::ExclusiveTimeMS, TEXT( "ExclusiveTimeMS" ), STRUCT_OFFSET( FEventGraphSample, _ExclusiveTimeMS ), EEventPropertyFormatters::TimeMS ), FEventProperty( EEventPropertyIndex::ExclusiveTimePct, TEXT( "ExclusiveTimePct" ), STRUCT_OFFSET( FEventGraphSample, _ExclusiveTimePct ), EEventPropertyFormatters::TimePct ), FEventProperty( EEventPropertyIndex::NumCallsPerFrame, TEXT( "NumCallsPerFrame" ), STRUCT_OFFSET( FEventGraphSample, _NumCallsPerFrame ), EEventPropertyFormatters::Number ), // Special none property FEventProperty( EEventPropertyIndex::None, NAME_None, INDEX_NONE, EEventPropertyFormatters::Name ), FEventProperty( EEventPropertyIndex::MinInclusiveTimeMS, TEXT( "MinInclusiveTimeMS" ), STRUCT_OFFSET( FEventGraphSample, _MinInclusiveTimeMS ), EEventPropertyFormatters::TimeMS ), FEventProperty( EEventPropertyIndex::MaxInclusiveTimeMS, TEXT( "MaxInclusiveTimeMS" ), STRUCT_OFFSET( FEventGraphSample, _MaxInclusiveTimeMS ), EEventPropertyFormatters::TimeMS ), FEventProperty( EEventPropertyIndex::AvgInclusiveTimeMS, TEXT( "AvgInclusiveTimeMS" ), STRUCT_OFFSET( FEventGraphSample, _AvgInclusiveTimeMS ), EEventPropertyFormatters::TimeMS ), FEventProperty( EEventPropertyIndex::MinNumCallsPerFrame, TEXT( "MinNumCallsPerFrame" ), STRUCT_OFFSET( FEventGraphSample, _MinNumCallsPerFrame ), EEventPropertyFormatters::Number ), FEventProperty( EEventPropertyIndex::MaxNumCallsPerFrame, TEXT( "MaxNumCallsPerFrame" ), STRUCT_OFFSET( FEventGraphSample, _MaxNumCallsPerFrame ), EEventPropertyFormatters::Number ), FEventProperty( EEventPropertyIndex::AvgNumCallsPerFrame, TEXT( "AvgNumCallsPerFrame" ), STRUCT_OFFSET( FEventGraphSample, _AvgNumCallsPerFrame ), EEventPropertyFormatters::Number ), FEventProperty( EEventPropertyIndex::ThreadName, TEXT( "ThreadName" ), STRUCT_OFFSET( FEventGraphSample, _ThreadName ), EEventPropertyFormatters::Name ), FEventProperty( EEventPropertyIndex::ThreadDurationMS, TEXT( "ThreadDurationMS" ), STRUCT_OFFSET( FEventGraphSample, _ThreadDurationMS ), EEventPropertyFormatters::TimeMS ), FEventProperty( EEventPropertyIndex::FrameDurationMS, TEXT( "FrameDurationMS" ), STRUCT_OFFSET( FEventGraphSample, _FrameDurationMS ), EEventPropertyFormatters::TimeMS ), FEventProperty( EEventPropertyIndex::ThreadPct, TEXT( "ThreadPct" ), STRUCT_OFFSET( FEventGraphSample, _ThreadPct ), EEventPropertyFormatters::TimePct ), FEventProperty( EEventPropertyIndex::FramePct, TEXT( "FramePct" ), STRUCT_OFFSET( FEventGraphSample, _FramePct ), EEventPropertyFormatters::TimePct ), FEventProperty( EEventPropertyIndex::ThreadToFramePct, TEXT( "ThreadToFramePct" ), STRUCT_OFFSET( FEventGraphSample, _ThreadToFramePct ), EEventPropertyFormatters::TimePct ), FEventProperty( EEventPropertyIndex::GroupName, TEXT( "GroupName" ), STRUCT_OFFSET( FEventGraphSample, _GroupName ), EEventPropertyFormatters::Name ), // Booleans FEventProperty(EEventPropertyIndex::bIsHotPath,TEXT("bIsHotPath"),STRUCT_OFFSET(FEventGraphSample,_bIsHotPath),EEventPropertyFormatters::Bool), FEventProperty(EEventPropertyIndex::bIsFiltered,TEXT("bIsFiltered"),STRUCT_OFFSET(FEventGraphSample,_bIsFiltered),EEventPropertyFormatters::Bool), FEventProperty(EEventPropertyIndex::bIsCulled,TEXT("bIsCulled"),STRUCT_OFFSET(FEventGraphSample,_bIsCulled),EEventPropertyFormatters::Bool), // Booleans internal FEventProperty(EEventPropertyIndex::bNeedNotCulledChildrenUpdate,TEXT("bNeedNotCulledChildrenUpdate"),STRUCT_OFFSET(FEventGraphSample,bNeedNotCulledChildrenUpdate),EEventPropertyFormatters::Bool), }; TMap FEventGraphSample::NamedProperties; void FEventGraphSample::InitializePropertyManagement() { static bool bInitialized = false; if( !bInitialized ) { NamedProperties = TMapBuilder() // Properties .Add( TEXT( "StatName" ), &Properties[(uint32)EEventPropertyIndex::StatName] ) .Add( TEXT( "InclusiveTimeMS" ), &Properties[(uint32)EEventPropertyIndex::InclusiveTimeMS] ) .Add( TEXT( "InclusiveTimePct" ), &Properties[(uint32)EEventPropertyIndex::InclusiveTimePct] ) .Add( TEXT( "ExclusiveTimeMS" ), &Properties[(uint32)EEventPropertyIndex::ExclusiveTimeMS] ) .Add( TEXT( "ExclusiveTimePct" ), &Properties[(uint32)EEventPropertyIndex::ExclusiveTimePct] ) .Add( TEXT( "NumCallsPerFrame" ), &Properties[(uint32)EEventPropertyIndex::NumCallsPerFrame] ) // Special none property .Add( NAME_None, &Properties[(uint32)EEventPropertyIndex::None] ) .Add( TEXT( "MinInclusiveTimeMS" ), &Properties[(uint32)EEventPropertyIndex::MinInclusiveTimeMS] ) .Add( TEXT( "MaxInclusiveTimeMS" ), &Properties[(uint32)EEventPropertyIndex::MaxInclusiveTimeMS] ) .Add( TEXT( "AvgInclusiveTimeMS" ), &Properties[(uint32)EEventPropertyIndex::AvgInclusiveTimeMS] ) .Add( TEXT( "MinNumCallsPerFrame" ), &Properties[(uint32)EEventPropertyIndex::MinNumCallsPerFrame] ) .Add( TEXT( "MaxNumCallsPerFrame" ), &Properties[(uint32)EEventPropertyIndex::MaxNumCallsPerFrame] ) .Add( TEXT( "AvgNumCallsPerFrame" ), &Properties[(uint32)EEventPropertyIndex::AvgNumCallsPerFrame] ) .Add( TEXT( "ThreadName" ), &Properties[(uint32)EEventPropertyIndex::ThreadName] ) .Add( TEXT( "ThreadDurationMS" ), &Properties[(uint32)EEventPropertyIndex::ThreadDurationMS] ) .Add( TEXT( "FrameDurationMS" ), &Properties[(uint32)EEventPropertyIndex::FrameDurationMS] ) .Add( TEXT( "ThreadPct" ), &Properties[(uint32)EEventPropertyIndex::ThreadPct] ) .Add( TEXT( "FramePct" ), &Properties[(uint32)EEventPropertyIndex::FramePct] ) .Add( TEXT( "ThreadToFramePct" ), &Properties[(uint32)EEventPropertyIndex::ThreadToFramePct] ) .Add( TEXT( "GroupName" ), &Properties[(uint32)EEventPropertyIndex::GroupName] ) // Booleans .Add( TEXT( "bIsHotPath" ), &Properties[(uint32)EEventPropertyIndex::bIsHotPath] ) .Add( TEXT( "bIsFiltered" ), &Properties[(uint32)EEventPropertyIndex::bIsFiltered] ) .Add( TEXT( "bIsCulled" ), &Properties[(uint32)EEventPropertyIndex::bIsCulled] ) // Booleans internal .Add( TEXT( "bNeedNotCulledChildrenUpdate" ), &Properties[(uint32)EEventPropertyIndex::bNeedNotCulledChildrenUpdate] ) ; // Make sure that the minimal property manager has been initialized. bInitialized = true; // Sanity checks. check( NamedProperties.FindChecked( NAME_None )->Name == NAME_None ); check( NamedProperties.FindChecked( NAME_None )->Offset == INDEX_NONE ); check( FEventGraphSample::Properties[ (uint32)EEventPropertyIndex::None ].Name == NAME_None ); check( FEventGraphSample::Properties[ (uint32)EEventPropertyIndex::None ].Offset == INDEX_NONE ); } } void FEventGraphSample::SetMaximumTimesForAllChildren() { struct FCopyMaximum { void operator()( FEventGraphSample* EventPtr, FEventGraphSample* RootEvent, FEventGraphSample* ThreadEvent ) { EventPtr->CopyMaximum( RootEvent, ThreadEvent ); } }; FEventGraphSamplePtr RootEvent = AsShared(); const int32 NumChildren = _ChildrenPtr.Num(); for (int32 ChildIndex = 0; ChildIndex < NumChildren; ++ChildIndex) { const FEventGraphSamplePtr& ThreadEvent = _ChildrenPtr[ChildIndex]; ThreadEvent->ExecuteOperationForAllChildren( FCopyMaximum(), (FEventGraphSample*)RootEvent.Get(), (FEventGraphSample*)ThreadEvent.Get() ); } } void FEventGraphSample::SetRootAndThreadForAllChildren() { struct FSetRootAndThread { void operator()( FEventGraphSample* EventPtr, FEventGraphSample* RootEvent, FEventGraphSample* ThreadEvent ) { EventPtr->_RootPtr = RootEvent; EventPtr->_ThreadPtr = ThreadEvent; } }; FEventGraphSamplePtr RootEvent = AsShared(); const int32 NumChildren = _ChildrenPtr.Num(); for (int32 ChildIndex = 0; ChildIndex < NumChildren; ++ChildIndex) { const FEventGraphSamplePtr& ThreadEvent = _ChildrenPtr[ChildIndex]; ThreadEvent->ExecuteOperationForAllChildren( FSetRootAndThread(), (FEventGraphSample*)RootEvent.Get(), (FEventGraphSample*)ThreadEvent.Get() ); } } void FEventGraphSample::FixChildrenTimesAndCalcAveragesForAllChildren( const double InNumFrames ) { struct FFixChildrenTimesAndCalcAverages { void operator()( FEventGraphSample* EventPtr, const double NumFrames ) { EventPtr->FixChildrenTimesAndCalcAverages( NumFrames ); } }; ExecuteOperationForAllChildren( FFixChildrenTimesAndCalcAverages(), InNumFrames ); } FEventGraphSamplePtr FEventGraphSample::FindChildPtr( const FEventGraphSamplePtr& OtherChild ) { FEventGraphSamplePtr FoundChildPtr; for( int32 ChildIndex = 0; ChildIndex < _ChildrenPtr.Num(); ++ChildIndex ) { const FEventGraphSamplePtr& ThisChild = _ChildrenPtr[ChildIndex]; const bool bTheSame = OtherChild->AreTheSamePtr( ThisChild ); if( bTheSame ) { FoundChildPtr = ThisChild; break; } } return FoundChildPtr; } void FEventGraphSample::Combine_Recurrent( const FEventGraphSamplePtr& Other ) { Combine( Other ); // Check other children. for( int32 ChildIndex = 0; ChildIndex < Other->_ChildrenPtr.Num(); ++ChildIndex ) { const FEventGraphSamplePtr& OtherChild = Other->_ChildrenPtr[ChildIndex]; FEventGraphSamplePtr ThisChild = FindChildPtr( OtherChild ); if( ThisChild.IsValid() ) { ThisChild->Combine_Recurrent( OtherChild ); } else { AddChildAndSetParentPtr( OtherChild->DuplicateWithHierarchyPtr() ); } } } /*----------------------------------------------------------------------------- FEventGraphData -----------------------------------------------------------------------------*/ FEventGraphData::FEventGraphData() : RootEvent( FEventGraphSample::CreateNamedEvent( FEventGraphConsts::RootEvent ) ) , FrameStartIndex( 0 ) , FrameEndIndex( 0 ) {} FEventGraphData::FEventGraphData( const FProfilerSession * const InProfilerSession, const uint32 InFrameIndex ) : FrameStartIndex( InFrameIndex ) , FrameEndIndex( InFrameIndex+1 ) { static FTotalTimeAndCount Current(0.0f, 0); PROFILER_SCOPE_LOG_TIME( TEXT( "FEventGraphData::FEventGraphData" ), &Current ); Description = FString::Printf( TEXT("%s: %i"), *InProfilerSession->GetShortName(), InFrameIndex ); // @TODO: Duplicate is not needed, remove it later. const TSharedRef& SessionDataProvider = InProfilerSession->GetDataProvider(); const TSharedRef DataProvider = SessionDataProvider->Duplicate( FrameStartIndex ); const double FrameDurationMS = DataProvider->GetFrameTimeMS( 0 ); const FProfilerSample& RootProfilerSample = DataProvider->GetCollection()[0]; RootEvent = FEventGraphSample::CreateNamedEvent( FEventGraphConsts::RootEvent ); PopulateHierarchy_Recurrent( InProfilerSession, RootEvent, RootProfilerSample, DataProvider ); // Root sample contains FrameDurationMS const TSharedRef& MetaData = InProfilerSession->GetMetaData(); RootEvent->_InclusiveTimeMS = MetaData->ConvertCyclesToMS( RootProfilerSample.GetDurationCycles() ); RootEvent->_MaxInclusiveTimeMS = RootEvent->_MinInclusiveTimeMS = RootEvent->_AvgInclusiveTimeMS = RootEvent->_InclusiveTimeMS; RootEvent->_InclusiveTimePct = 100.0f; RootEvent->_MinNumCallsPerFrame = RootEvent->_MaxNumCallsPerFrame = RootEvent->_AvgNumCallsPerFrame = RootEvent->_NumCallsPerFrame; // Set root and thread event. RootEvent->SetRootAndThreadForAllChildren(); // Fix all children. const double MyNumFrames = 1.0; RootEvent->FixChildrenTimesAndCalcAveragesForAllChildren( MyNumFrames ); } void FEventGraphData::PopulateHierarchy_Recurrent ( const FProfilerSession * const ProfilerSession, const FEventGraphSamplePtr ParentEvent, const FProfilerSample& ParentSample, const TSharedRef DataProvider ) { const TSharedRef& MetaData = ProfilerSession->GetMetaData(); for( int32 ChildIndex = 0; ChildIndex < ParentSample.ChildrenIndices().Num(); ChildIndex++ ) { const FProfilerSample& ChildSample = DataProvider->GetCollection()[ ParentSample.ChildrenIndices()[ChildIndex] ]; const FProfilerStat& ProfilerThread = MetaData->GetStatByID( ChildSample.ThreadID() ); const FName& ThreadName = ProfilerThread.Name(); const FProfilerStat& ProfilerStat = MetaData->GetStatByID( ChildSample.StatID() ); const FName& StatName = ProfilerStat.Name(); const FName& GroupName = ProfilerStat.OwningGroup().Name(); FEventGraphSample* ChildEvent = new FEventGraphSample ( ThreadName, GroupName, ChildSample.StatID(), StatName, MetaData->ConvertCyclesToMS( ChildSample.GetDurationCycles() ), (double)ChildSample.GetCallCount(), ParentEvent ); FEventGraphSamplePtr ChildEventPtr = MakeShareable( ChildEvent ); ParentEvent->AddChildPtr( ChildEventPtr ); PopulateHierarchy_Recurrent( ProfilerSession, ChildEventPtr, ChildSample, DataProvider ); } } FEventGraphData::FEventGraphData( const FEventGraphData& Source ) : FrameStartIndex( Source.FrameStartIndex ) , FrameEndIndex( Source.FrameEndIndex ) { RootEvent = Source.GetRoot()->DuplicateWithHierarchyPtr(); RootEvent->SetRootAndThreadForAllChildren(); } FEventGraphDataRef FEventGraphData::DuplicateAsRef() { FEventGraphDataRef EventGraphRef = MakeShareable( new FEventGraphData(*this) ); return EventGraphRef; } void FEventGraphData::Combine( const FEventGraphData& Other ) { RootEvent->Combine_Recurrent( Other.GetRoot() ); Description = FString::Printf( TEXT("Combine: %i"), GetNumFrames() ); } void FEventGraphData::SetAsAverage() { struct FCopyAverage { void operator()( FEventGraphSample* EventPtr, const double NumFrames ) { EventPtr->CopyAverage( NumFrames ); } }; const double NumFrames = (double)GetNumFrames(); RootEvent->ExecuteOperationForAllChildren( FCopyAverage(), NumFrames ); Description = FString::Printf( TEXT("Average: %i"), GetNumFrames() ); } void FEventGraphData::SetAsMaximim() { RootEvent->SetMaximumTimesForAllChildren(); Description = FString::Printf( TEXT( "Maximum: %i" ), GetNumFrames() ); } void FEventGraphData::Finalize( const uint32 InFrameStartIndex, const uint32 InFrameEndIndex ) { FrameStartIndex = InFrameStartIndex; FrameEndIndex = InFrameEndIndex; const double NumFrames = (double)GetNumFrames(); // Set root and thread event. RootEvent->SetRootAndThreadForAllChildren(); // Fix all children. RootEvent->FixChildrenTimesAndCalcAveragesForAllChildren( NumFrames ); } //}//namespace NEventGraphSample FString EEventGraphTypes::ToName( const EEventGraphTypes::Type EventGraphType ) { switch( EventGraphType ) { case Average: return LOCTEXT("EventGraphType_Name_Average", "Average").ToString(); case Maximum: return LOCTEXT("EventGraphType_Name_Maximum", "Maximum").ToString(); case OneFrame: return LOCTEXT("EventGraphType_Name_OneFrame", "OneFrame").ToString(); case Total: return LOCTEXT( "EventGraphType_Name_Total", "Total" ).ToString(); default: return LOCTEXT("InvalidOrMax", "InvalidOrMax").ToString(); } } FString EEventGraphTypes::ToDescription( const EEventGraphTypes::Type EventGraphType ) { switch( EventGraphType ) { case Average: return LOCTEXT("EventGraphType_Desc_Average", "Per-frame average event graph").ToString(); case Maximum: return LOCTEXT("EventGraphType_Desc_Maximum", "Highest \"per-frame\" event graph").ToString(); case OneFrame: return LOCTEXT("EventGraphType_Desc_OneFrame", "Event graph for one frame").ToString(); case Total: return LOCTEXT( "EventGraphType_Desc_Total", "Event graph for selected frames" ).ToString(); default: return LOCTEXT("InvalidOrMax", "InvalidOrMax").ToString(); } } #undef LOCTEXT_NAMESPACE #endif // STATS