void FEventGraphData::PopulateHierarchy_Recurrent ( const FProfilerSession * const ProfilerSession, const FEventGraphSamplePtr ParentEvent, const FProfilerSample& ParentSample, const IDataProviderRef DataProvider ) { const FProfilerStatMetaDataRef& 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 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::PopulateHierarchy_Recurrent ( const FProfilerCycleGraph& ParentGraph, const double ParentStartTimeMS, const double ParentDurationMS, const uint32 ParentSampleIndex ) { const FProfilerStatMetaDataRef MetaData = GetMetaData(); const uint32& ThreadID = MetaData->ThreadIDtoStatID.FindChecked( ParentGraph.ThreadId ); const uint32 SampleIndex = DataProvider->AddHierarchicalSample ( ThreadID, MetaData->GetStatByID(ParentGraph.StatId).OwningGroup().ID(), ParentGraph.StatId, ParentStartTimeMS, ParentDurationMS, ParentGraph.CallsPerFrame, ParentSampleIndex ); double ChildStartTimeMS = ParentStartTimeMS; double ChildrenDurationMS = 0.0f; for( int32 DataIndex = 0; DataIndex < ParentGraph.Children.Num(); DataIndex++ ) { const FProfilerCycleGraph& ChildCyclesCounter = ParentGraph.Children[DataIndex]; const double ChildDurationMS = MetaData->ConvertCyclesToMS( ChildCyclesCounter.Value ); if (ChildDurationMS > 0.0) { PopulateHierarchy_Recurrent( ChildCyclesCounter, ChildStartTimeMS, ChildDurationMS, SampleIndex ); } ChildStartTimeMS += ChildDurationMS; ChildrenDurationMS += ChildDurationMS; } const double SelfTimeMS = ParentDurationMS - ChildrenDurationMS; if( SelfTimeMS > 0.0f && ParentGraph.Children.Num() > 0 ) { const FName& ParentStatName = MetaData->GetStatByID( ParentGraph.StatId ).Name(); const FName& ParentGroupName = MetaData->GetStatByID( ParentGraph.StatId ).OwningGroup().Name(); // Create a fake stat that represents this profiler sample's exclusive time. // This is required if we want to create correct combined event graphs later. DataProvider->AddHierarchicalSample ( ThreadID, MetaData->GetStatByID(0).OwningGroup().ID(), 0, // @see FProfilerStatMetaData.Update, 0 means "Self" ChildStartTimeMS, SelfTimeMS, 1, SampleIndex ); } }
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; }