void FProfilerManager::LoadProfilerCapture( const FString& ProfilerCaptureFilepath, const bool bAdd /*= false*/ ) { // deselect the active session if (ActiveSession.IsValid()) { SessionManager->SelectSession(NULL); } if( bAdd == false ) { ClearStatsAndInstances(); } FProfilerSessionRef ProfilerSession = MakeShareable( new FProfilerSession( ProfilerCaptureFilepath ) ); auto Var = ProfilerSession->AsShared(); const FGuid ProfilerInstanceID = ProfilerSession->GetInstanceID(); ProfilerSession-> SetOnCaptureFileProcessed( FProfilerSession::FCaptureFileProcessedDelegate::CreateSP( this, &FProfilerManager::ProfilerSession_OnCaptureFileProcessed ) ) .SetOnAddThreadTime( FProfilerSession::FAddThreadTimeDelegate::CreateSP( this, &FProfilerManager::ProfilerSession_OnAddThreadTime ) ); ProfilerSessionInstances.Add( ProfilerInstanceID, ProfilerSession ); { PROFILER_SCOPE_LOG_TIME( TEXT( "ProfilerClient->LoadCapture" ), nullptr ); ProfilerClient->LoadCapture( ProfilerCaptureFilepath, ProfilerInstanceID ); } SessionInstancesUpdatedEvent.Broadcast(); ProfilerType = EProfilerSessionTypes::StatsFile; GetProfilerWindow()->ManageEventGraphTab( ProfilerInstanceID, true, ProfilerSession->GetName() ); SetViewMode( EProfilerViewMode::LineIndexBased ); }
void FProfilerSession::UpdateAggregatedStats( const uint32 FrameIndex ) { static FTotalTimeAndCount TimeAndCount( 0.0f, 0 ); PROFILER_SCOPE_LOG_TIME( TEXT( "2 FProfilerSession::UpdateAggregatedStats" ), &TimeAndCount ); 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 uint32 StatID = ProfilerSample.StatID(); FProfilerAggregatedStat* AggregatedStat = AggregatedStats.Find( StatID ); if( !AggregatedStat ) { const FProfilerStat& ProfilerStat = GetMetaData()->GetStatByID( StatID ); AggregatedStat = &AggregatedStats.Add( ProfilerSample.StatID(), FProfilerAggregatedStat( ProfilerStat.Name(), ProfilerStat.OwningGroup().Name(), ProfilerSample.Type() ) ); } (*AggregatedStat) += ProfilerSample; } for( auto It = AggregatedStats.CreateIterator(); It; ++It ) { FProfilerAggregatedStat& AggregatedStat = It.Value(); AggregatedStat.Advance(); } // @TODO: Create map for stats TMap<uint32, TArray<uint32> >; StatID -> Sample indices for faster lookup in data providers }
void FProfilerStatMetaData::Update( const FStatMetaData& ClientStatMetaData ) { PROFILER_SCOPE_LOG_TIME( TEXT( "FProfilerStatMetaData.Update" ), nullptr ); // Iterate through all thread descriptions. ThreadDescriptions.Append( ClientStatMetaData.ThreadDescriptions ); // Initialize fake stat for Self. const uint32 NoGroupID = 0; InitializeGroup( NoGroupID, "NoGroup" ); InitializeStat( 0, NoGroupID, TEXT( "Self" ), STATTYPE_CycleCounter ); InitializeStat( 1, NoGroupID, FStatConstants::NAME_ThreadRoot.GetPlainNameString(), STATTYPE_CycleCounter, FStatConstants::NAME_ThreadRoot ); // Iterate through all stat group descriptions. for( auto It = ClientStatMetaData.GroupDescriptions.CreateConstIterator(); It; ++It ) { const FStatGroupDescription& GroupDesc = It.Value(); InitializeGroup( GroupDesc.ID, GroupDesc.Name ); } // Iterate through all stat descriptions. for( auto It = ClientStatMetaData.StatDescriptions.CreateConstIterator(); It; ++It ) { const FStatDescription& StatDesc = It.Value(); InitializeStat( StatDesc.ID, StatDesc.GroupID, StatDesc.Name, (EStatType)StatDesc.StatType ); } SecondsPerCycle = ClientStatMetaData.SecondsPerCycle; }
FEventGraphDataRef FProfilerSession::CreateEventGraphData( const uint32 FrameStartIndex, const uint32 FrameEndIndex, const EEventGraphTypes::Type EventGraphType ) { static FTotalTimeAndCount Current(0.0f, 0); PROFILER_SCOPE_LOG_TIME( TEXT( "FProfilerSession::CreateEventGraphData" ), &Current ); FEventGraphData* EventGraphData = new FEventGraphData(); if( EventGraphType == EEventGraphTypes::Average ) { for( uint32 FrameIndex = FrameStartIndex; FrameIndex < FrameEndIndex+1; ++FrameIndex ) { // Create a temporary event graph data for the specified frame. const FEventGraphData CurrentEventGraphData( AsShared(), FrameIndex ); EventGraphData->CombineAndAdd( CurrentEventGraphData ); } EventGraphData->Advance( FrameStartIndex, FrameEndIndex+1 ); EventGraphData->Divide( (double)EventGraphData->GetNumFrames() ); } else if( EventGraphType == EEventGraphTypes::Maximum ) { for( uint32 FrameIndex = FrameStartIndex; FrameIndex < FrameEndIndex+1; ++FrameIndex ) { // Create a temporary event graph data for the specified frame. const FEventGraphData CurrentEventGraphData( AsShared(), FrameIndex ); EventGraphData->CombineAndFindMax( CurrentEventGraphData ); } EventGraphData->Advance( FrameStartIndex, FrameEndIndex+1 ); } return MakeShareable( EventGraphData ); }
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 IDataProviderRef& SessionDataProvider = InProfilerSession->GetDataProvider(); const IDataProviderRef DataProvider = SessionDataProvider->Duplicate<FArrayDataProvider>( 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 FProfilerStatMetaDataRef& 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 FProfilerSession::CompletionSyncAggregatedEventGraphData() { if (CompletionSync.GetReference() && !CompletionSync->IsComplete()) { static FTotalTimeAndCount JoinTasksTimeAndCont( 0.0f, 0 ); PROFILER_SCOPE_LOG_TIME( TEXT( "4 FProfilerSession::CombineJoinAndContinue" ), &JoinTasksTimeAndCont ); FTaskGraphInterface::Get().WaitUntilTaskCompletes( CompletionSync, ENamedThreads::GameThread ); } }
void FProfilerSession::UpdateAggregatedEventGraphData( const uint32 FrameIndex ) { static FTotalTimeAndCount TimeAndCount( 0.0f, 0 ); PROFILER_SCOPE_LOG_TIME( TEXT( "3 FProfilerSession::UpdateAggregatedEventGraphData" ), &TimeAndCount ); CompletionSyncAggregatedEventGraphData(); // Create a temporary event graph data for the specified frame. delete EventGraphDataCurrent; EventGraphDataCurrent = new FEventGraphData( AsShared(), FrameIndex ); const uint32 NumFramesLocal = SessionType == EProfilerSessionTypes::Live ? DataProvider->GetNumFrames() : 0; static const bool bUseTaskGraph = true; if( bUseTaskGraph ) { FGraphEventArray EventGraphCombineTasks; DECLARE_CYCLE_STAT(TEXT("FSimpleDelegateGraphTask.EventGraphData.CombineAndFindMax"), STAT_FSimpleDelegateGraphTask_EventGraphData_CombineAndFindMax, STATGROUP_TaskGraphTasks); DECLARE_CYCLE_STAT(TEXT("FSimpleDelegateGraphTask.EventGraphData.CombineAndAdd"), STAT_FSimpleDelegateGraphTask_EventGraphData_EventGraphCombineAndAdd, STATGROUP_TaskGraphTasks); new (EventGraphCombineTasks) FGraphEventRef(FSimpleDelegateGraphTask::CreateAndDispatchWhenReady ( FSimpleDelegateGraphTask::FDelegate::CreateRaw( this, &FProfilerSession::EventGraphCombineAndMax, EventGraphDataCurrent, NumFramesLocal ), GET_STATID(STAT_FSimpleDelegateGraphTask_EventGraphData_CombineAndFindMax), nullptr )); new (EventGraphCombineTasks) FGraphEventRef(FSimpleDelegateGraphTask::CreateAndDispatchWhenReady ( FSimpleDelegateGraphTask::FDelegate::CreateRaw( this, &FProfilerSession::EventGraphCombineAndAdd, EventGraphDataCurrent, NumFramesLocal ), GET_STATID(STAT_FSimpleDelegateGraphTask_EventGraphData_EventGraphCombineAndAdd), nullptr )); DECLARE_CYCLE_STAT(TEXT("FNullGraphTask.EventGraphData.CombineJoinAndContinue"), STAT_FNullGraphTask_EventGraphData_CombineJoinAndContinue, STATGROUP_TaskGraphTasks); // JoinThreads CompletionSync = TGraphTask<FNullGraphTask>::CreateTask( &EventGraphCombineTasks, ENamedThreads::GameThread ) .ConstructAndDispatchWhenReady(GET_STATID(STAT_FNullGraphTask_EventGraphData_CombineJoinAndContinue), ENamedThreads::AnyThread); } else { EventGraphCombineAndMax( EventGraphDataCurrent, NumFramesLocal ); EventGraphCombineAndAdd( EventGraphDataCurrent, NumFramesLocal ); } }
void FProfilerManager::DataGraph_OnSelectionChangedForIndex( uint32 FrameStartIndex, uint32 FrameEndIndex ) { PROFILER_SCOPE_LOG_TIME( TEXT( "FProfilerManager::DataGraph_OnSelectionChangedForIndex" ), nullptr ); for( auto It = GetProfilerInstancesIterator(); It; ++It ) { FProfilerSessionRef ProfilerSession = It.Value(); FEventGraphDataRef EventGraphDataAverage = ProfilerSession->CreateEventGraphData( FrameStartIndex, FrameEndIndex, EEventGraphTypes::Average ); FEventGraphDataRef EventGraphDataMaximum = ProfilerSession->CreateEventGraphData( FrameStartIndex, FrameEndIndex, EEventGraphTypes::Maximum ); GetProfilerWindow()->UpdateEventGraph( ProfilerSession->GetInstanceID(), EventGraphDataAverage, EventGraphDataMaximum, false ); } }
bool FProfilerSession::HandleTicker( float DeltaTime ) { // Update metadata if needed if( bRequestStatMetadataUpdate ) { StatMetaData->Update( ClientStatMetadata ); bRequestStatMetadataUpdate = false; } static double ProcessingTime = 1.0; ProcessingTime -= DeltaTime; static int32 NumFramesProcessedLastTime = 0; if (ProcessingTime < 0.0) { UE_LOG( LogStats, Verbose, TEXT( "NumFramesProcessedLastTime: %4i / %4i" ), NumFramesProcessedLastTime, FrameToProcess.Num() ); ProcessingTime = 1.0; NumFramesProcessedLastTime = 0; } // Limit processing to 50ms per frame. const double TimeLimit = 50 / 1000.0; double Seconds = 0; for( int32 FrameNumber = 0; FrameNumber < FrameToProcess.Num(); FrameNumber++ ) { if( Seconds > TimeLimit ) { break; } static FTotalTimeAndCount Current( 0.0f, 0 ); PROFILER_SCOPE_LOG_TIME( TEXT( "1 FProfilerSession::HandleTicker" ), &Current ); NumFramesProcessedLastTime++; NumFramesProcessed++; FSimpleScopeSecondsCounter SecondsCounter(Seconds); const uint32 FrameIndex = FrameToProcess[0]; FrameToProcess.RemoveAt( 0 ); FProfilerDataFrame& CurrentProfilerData = FrameToProfilerDataMapping.FindChecked( FrameIndex ); TMap<uint32, float> ThreadMS; // Preprocess the hierarchical samples for the specified frame. const TMap<uint32, FProfilerCycleGraph>& CycleGraphs = CurrentProfilerData.CycleGraphs; // Add a root sample for this frame. // HACK 2013-07-19, 13:44 STAT_Root == ThreadRoot in stats2 const uint32 FrameRootSampleIndex = DataProvider->AddHierarchicalSample( 0, StatMetaData->GetStatByID(1).OwningGroup().ID(), 1, 0.0f, 0.0f, 1 ); double GameThreadTimeMS = 0.0f; double MaxThreadTimeMS = 0.0f; double ThreadStartTimeMS = 0.0; for( auto ThreadIt = CycleGraphs.CreateConstIterator(); ThreadIt; ++ThreadIt ) { const uint32 ThreadID = ThreadIt.Key(); const FProfilerCycleGraph& ThreadGraph = ThreadIt.Value(); // Calculate total time for this thread. double ThreadDurationTimeMS = 0.0; ThreadStartTimeMS = CurrentProfilerData.FrameStart; for( int32 Index = 0; Index < ThreadGraph.Children.Num(); Index++ ) { ThreadDurationTimeMS += StatMetaData->ConvertCyclesToMS( ThreadGraph.Children[Index].Value ); } if (ThreadDurationTimeMS > 0.0) { // Check for game thread. const FString GameThreadName = FName( NAME_GameThread ).GetPlainNameString(); const bool bGameThreadFound = StatMetaData->GetThreadDescriptions().FindChecked( ThreadID ).Contains( GameThreadName ); if( bGameThreadFound ) { GameThreadTimeMS = ThreadDurationTimeMS; } // Add a root sample for each thread. const uint32& NewThreadID = StatMetaData->ThreadIDtoStatID.FindChecked( ThreadID ); const uint32 ThreadRootSampleIndex = DataProvider->AddHierarchicalSample ( NewThreadID/*ThreadID*/, StatMetaData->GetStatByID(NewThreadID).OwningGroup().ID(), NewThreadID, ThreadStartTimeMS, ThreadDurationTimeMS, 1, FrameRootSampleIndex ); ThreadMS.FindOrAdd( ThreadID ) = (float)ThreadDurationTimeMS; // Recursively add children and parent to the root samples. for( int32 Index = 0; Index < ThreadGraph.Children.Num(); Index++ ) { const FProfilerCycleGraph& CycleGraph = ThreadGraph.Children[Index]; const double CycleDurationMS = StatMetaData->ConvertCyclesToMS( CycleGraph.Value ); const double CycleStartTimeMS = ThreadStartTimeMS; if (CycleDurationMS > 0.0) { PopulateHierarchy_Recurrent( CycleGraph, CycleStartTimeMS, CycleDurationMS, ThreadRootSampleIndex ); } ThreadStartTimeMS += CycleDurationMS; } MaxThreadTimeMS = FMath::Max( MaxThreadTimeMS, ThreadDurationTimeMS ); } } // Fix the root stat time. FProfilerSampleArray& MutableCollection = const_cast<FProfilerSampleArray&>(DataProvider->GetCollection()); MutableCollection[FrameRootSampleIndex].SetDurationMS( GameThreadTimeMS != 0.0f ? GameThreadTimeMS : MaxThreadTimeMS ); FPSAnalyzer->AddSample( GameThreadTimeMS > 0.0f ? 1000.0f/GameThreadTimeMS : 0.0f ); // Process the non-hierarchical samples for the specified frame. { // Process integer counters. for( int32 Index = 0; Index < CurrentProfilerData.CountAccumulators.Num(); Index++ ) { const FProfilerCountAccumulator& IntCounter = CurrentProfilerData.CountAccumulators[Index]; const EProfilerSampleTypes::Type ProfilerSampleType = StatMetaData->GetSampleTypeForStatID( IntCounter.StatId ); DataProvider->AddCounterSample( StatMetaData->GetStatByID(IntCounter.StatId).OwningGroup().ID(), IntCounter.StatId, (double)IntCounter.Value, ProfilerSampleType ); } // Process floating point counters. for( int32 Index = 0; Index < CurrentProfilerData.FloatAccumulators.Num(); Index++ ) { const FProfilerFloatAccumulator& FloatCounter = CurrentProfilerData.FloatAccumulators[Index]; DataProvider->AddCounterSample( StatMetaData->GetStatByID(FloatCounter.StatId).OwningGroup().ID(), FloatCounter.StatId, (double)FloatCounter.Value, EProfilerSampleTypes::NumberFloat ); } } // Advance frame const uint32 LastFrameIndex = DataProvider->GetNumFrames(); DataProvider->AdvanceFrame( MaxThreadTimeMS ); // Update aggregated stats UpdateAggregatedStats( LastFrameIndex ); // Update aggregated events. UpdateAggregatedEventGraphData( LastFrameIndex ); { static FTotalTimeAndCount TotalOnAddThreadTime( 0.0f, 0 ); PROFILER_SCOPE_LOG_TIME( TEXT( "5 FProfilerSession::HandleTicker.OnAddThreadTime" ), &Current ); // Update mini-view. OnAddThreadTime.ExecuteIfBound( LastFrameIndex, ThreadMS, StatMetaData ); } FrameToProfilerDataMapping.Remove( FrameIndex ); } if( SessionType == EProfilerSessionTypes::StatsFile ) { if( FrameToProcess.Num() == 0 && bHasAllProfilerData ) { CompletionSyncAggregatedEventGraphData(); // Advance event graphs. EventGraphDataMaximum->Advance( 0, DataProvider->GetNumFrames() ); EventGraphDataTotal->Advance( 0, DataProvider->GetNumFrames() ); // Broadcast that a capture file has been fully processed. OnCaptureFileProcessed.ExecuteIfBound( GetInstanceID() ); // Disable tick method as we no longer need to tick. return false; } } return true; }