// Copyright Epic Games, Inc. All Rights Reserved. #include "TraceServices/Model/LoadTimeProfiler.h" #include "Model/LoadTimeProfilerPrivate.h" #include "AnalysisServicePrivate.h" #include "Common/TimelineStatistics.h" #include "TraceServices/Common/CancellationToken.h" namespace TraceServices { FLoadTimeProfilerProvider::FLoaderFrameCounter::FLoaderFrameCounter(ELoaderFrameCounterType InType, const TPagedArray& InFrames) : Frames(InFrames) , Type(InType) { } const TCHAR* FLoadTimeProfilerProvider::FLoaderFrameCounter::GetName() const { switch (Type) { case LoaderFrameCounterType_IoDispatcherThroughput: return TEXT("AssetLoading/IoDispatcher/Throughput"); case LoaderFrameCounterType_LoaderThroughput: return TEXT("AssetLoading/AsyncLoading/Throughput"); } check(false); return TEXT(""); } const TCHAR* FLoadTimeProfilerProvider::FLoaderFrameCounter::GetGroup() const { return nullptr; } const TCHAR* FLoadTimeProfilerProvider::FLoaderFrameCounter::GetDescription() const { return TEXT("N/A"); } bool FLoadTimeProfilerProvider::FLoaderFrameCounter::IsFloatingPoint() const { switch (Type) { case LoaderFrameCounterType_IoDispatcherThroughput: case LoaderFrameCounterType_LoaderThroughput: return true; } return false; } ECounterDisplayHint FLoadTimeProfilerProvider::FLoaderFrameCounter::GetDisplayHint() const { switch (Type) { case LoaderFrameCounterType_IoDispatcherThroughput: case LoaderFrameCounterType_LoaderThroughput: return CounterDisplayHint_Memory; } return CounterDisplayHint_None; } void FLoadTimeProfilerProvider::FLoaderFrameCounter::EnumerateValues(double IntervalStart, double IntervalEnd, bool bIncludeExternalBounds, TFunctionRef Callback) const { unimplemented(); } void FLoadTimeProfilerProvider::FLoaderFrameCounter::EnumerateFloatValues(double IntervalStart, double IntervalEnd, bool bIncludeExternalBounds, TFunctionRef Callback) const { if (Frames.Num() == 0) { return; } int32 FirstFrameIndex = FMath::Max(0, int32(FMath::RoundToZero(IntervalStart / FLoadTimeProfilerProvider::LoaderFrameLength)) - 1); int32 LastFrameIndex = FMath::Min(int32(Frames.Num()) - 1, int32(FMath::RoundToZero(IntervalEnd / FLoadTimeProfilerProvider::LoaderFrameLength)) + 1); for (int32 FrameIndex = FirstFrameIndex; FrameIndex <= LastFrameIndex; ++FrameIndex) { double Time = (FrameIndex + 0.5) * FLoadTimeProfilerProvider::LoaderFrameLength; if (bIncludeExternalBounds || (IntervalStart <= Time && Time <= IntervalEnd)) { const FLoaderFrame& Frame = Frames[FrameIndex]; switch (Type) { case LoaderFrameCounterType_LoaderThroughput: Callback(Time, double(Frame.HeaderLoadedBytes + Frame.ExportLoadedBytes) / FLoadTimeProfilerProvider::LoaderFrameLength); break; case LoaderFrameCounterType_IoDispatcherThroughput: Callback(Time, double(Frame.IoDispatcherReadBytes) / FLoadTimeProfilerProvider::LoaderFrameLength); break; } } } } void FLoadTimeProfilerProvider::FLoaderFrameCounter::EnumerateOps(double IntervalStart, double IntervalEnd, bool bIncludeExternalBounds, TFunctionRef Callback) const { unimplemented(); } void FLoadTimeProfilerProvider::FLoaderFrameCounter::EnumerateFloatOps(double IntervalStart, double IntervalEnd, bool bIncludeExternalBounds, TFunctionRef Callback) const { unimplemented(); } FLoadTimeProfilerProvider::FLoadTimeProfilerProvider(IAnalysisSession& InSession, IEditableCounterProvider& InEditableCounterProvider) : Session(InSession) , EditableCounterProvider(InEditableCounterProvider) , ClassInfos(Session.GetLinearAllocator(), 16384) , Requests(Session.GetLinearAllocator(), 16384) , Packages(Session.GetLinearAllocator(), 16384) , IoDispatcherBatches(Session.GetLinearAllocator(), 16384) , Exports(Session.GetLinearAllocator(), 16384) , RequestsTable(Requests) , Frames(Session.GetLinearAllocator(), 4096) { RequestsTable.EditLayout(). AddColumn(&FLoadRequest::Name, TEXT("Name")). AddColumn(&FLoadRequest::ThreadId, TEXT("ThreadId")). AddColumn(&FLoadRequest::StartTime, TEXT("StartTime"), TableColumnDisplayHint_Time). AddColumn(&FLoadRequest::EndTime, TEXT("EndTime"), TableColumnDisplayHint_Time). AddColumn( [](const FLoadRequest& Row) { return Row.Packages.Num(); }, TEXT("PackageCount"), TableColumnDisplayHint_Summable). AddColumn(&FLoadTimeProfilerProvider::PackageSizeSum, TEXT("Size"), TableColumnDisplayHint_Memory | TableColumnDisplayHint_Summable). AddColumn( [](const FLoadRequest& Row) { return Row.Packages.Num() ? Row.Packages[0]->Name : TEXT("N/A"); }, TEXT("FirstPackage")); AggregatedStatsTableLayout. AddColumn(&FLoadTimeProfilerAggregatedStats::Name, TEXT("Name")). AddColumn(&FLoadTimeProfilerAggregatedStats::Count, TEXT("Count"), TableColumnDisplayHint_Summable). AddColumn(&FLoadTimeProfilerAggregatedStats::Total, TEXT("Total"), TableColumnDisplayHint_Time | TableColumnDisplayHint_Summable). AddColumn(&FLoadTimeProfilerAggregatedStats::Min, TEXT("Min"), TableColumnDisplayHint_Time). AddColumn(&FLoadTimeProfilerAggregatedStats::Max, TEXT("Max"), TableColumnDisplayHint_Time). AddColumn(&FLoadTimeProfilerAggregatedStats::Average, TEXT("Avg"), TableColumnDisplayHint_Time). AddColumn(&FLoadTimeProfilerAggregatedStats::Median, TEXT("Med"), TableColumnDisplayHint_Time); PackagesTableLayout. AddColumn([](const FPackagesTableRow& Row) { return Row.PackageInfo->Name; }, TEXT("Package")). AddColumn([](const FPackagesTableRow& Row) { return Row.PackageInfo ? Row.PackageInfo->RequestId : 0; }, TEXT("RequestId")). AddColumn(&FPackagesTableRow::TotalSerializedSize, TEXT("SerializedSize"), TableColumnDisplayHint_Memory | TableColumnDisplayHint_Summable). AddColumn(&FPackagesTableRow::SerializedHeaderSize, TEXT("SerializedHeaderSize"), TableColumnDisplayHint_Memory | TableColumnDisplayHint_Summable). AddColumn(&FPackagesTableRow::SerializedExportsCount, TEXT("SerializedExportsCount"), TableColumnDisplayHint_Summable). AddColumn(&FPackagesTableRow::SerializedExportsSize, TEXT("SerializedExportsSize"), TableColumnDisplayHint_Memory | TableColumnDisplayHint_Summable). AddColumn(&FPackagesTableRow::MainThreadTime, TEXT("MainThreadTime"), TableColumnDisplayHint_Time | TableColumnDisplayHint_Summable). AddColumn(&FPackagesTableRow::AsyncLoadingThreadTime, TEXT("AsyncLoadingThreadTime"), TableColumnDisplayHint_Time | TableColumnDisplayHint_Summable). AddColumn([](const FPackagesTableRow& Row) { return Row.PackageInfo ? Row.PackageInfo->ImportedPackages.Num() : 0; }, TEXT("ImportedPackagesCount"), TableColumnDisplayHint_Summable); ExportsTableLayout. AddColumn([](const FExportsTableRow& Row) { return Row.ExportInfo->Package ? Row.ExportInfo->Package->Name : TEXT("[unknown]"); }, TEXT("Package")). AddColumn([](const FExportsTableRow& Row) { return Row.ExportInfo->Package ? Row.ExportInfo->Package->RequestId : 0; }, TEXT("RequestId")). AddColumn([](const FExportsTableRow& Row) { return Row.ExportInfo->Class ? Row.ExportInfo->Class->Name : TEXT("[unknown]"); }, TEXT("Class")). AddColumn([](const FExportsTableRow& Row) { return GetLoadTimeProfilerObjectEventTypeString(Row.EventType); }, TEXT("EventType")). AddColumn(&FExportsTableRow::SerializedSize, TEXT("SerializedSize"), TableColumnDisplayHint_Memory | TableColumnDisplayHint_Summable). AddColumn(&FExportsTableRow::MainThreadTime, TEXT("MainThreadTime"), TableColumnDisplayHint_Time | TableColumnDisplayHint_Summable). AddColumn(&FExportsTableRow::AsyncLoadingThreadTime, TEXT("AsyncLoadingThreadTime"), TableColumnDisplayHint_Time | TableColumnDisplayHint_Summable); RequestsTableLayout. AddColumn(&FRequestsTableRow::Id, TEXT("Id")). AddColumn(&FRequestsTableRow::Name, TEXT("Name")). AddColumn(&FRequestsTableRow::StartTime, TEXT("StartTime"), TableColumnDisplayHint_Time). AddColumn(&FRequestsTableRow::Duration, TEXT("Duration"), TableColumnDisplayHint_Time | TableColumnDisplayHint_Summable). AddColumn( [](const FRequestsTableRow& Row) { return Row.Packages.Num(); }, TEXT("PackageCount"), TableColumnDisplayHint_Summable). AddColumn([](const FRequestsTableRow& Row) { uint64 Sum = 0; for (const FPackageInfo* Package : Row.Packages) { Sum += Package->Summary.TotalHeaderSize; for (const FPackageExportInfo* Export : Package->Exports) { Sum += Export->SerialSize; } } return Sum; }, TEXT("SerializedSize"), TableColumnDisplayHint_Memory | TableColumnDisplayHint_Summable). AddColumn( [](const FRequestsTableRow& Row) { return Row.Packages.Num() ? Row.Packages[0]->Name : TEXT("N/A"); }, TEXT("FirstPackage")); } bool FLoadTimeProfilerProvider::GetCpuThreadTimelineIndex(uint32 ThreadId, uint32& OutTimelineIndex) const { Session.ReadAccessCheck(); const uint32* FindTimelineIndex = CpuTimelinesThreadMap.Find(ThreadId); if (FindTimelineIndex) { OutTimelineIndex = *FindTimelineIndex; return true; } else { return false; } } bool FLoadTimeProfilerProvider::ReadTimeline(uint32 Index, TFunctionRef Callback) const { Session.ReadAccessCheck(); if (Index >= uint32(CpuTimelines.Num())) { return false; } Callback(*CpuTimelines[Index]); return true; } ITable* FLoadTimeProfilerProvider::CreateEventAggregation(double IntervalStart, double IntervalEnd) const { TArray Timelines; for (const TSharedRef& Timeline : CpuTimelines) { Timelines.Add(&Timeline.Get()); } auto BucketMapper = [](const FLoadTimeProfilerCpuEvent& Event) { return Event.Package; }; TMap Aggregation; // Cancellation not currently implemented for LoadTimeProfilerProvider. TSharedPtr bIsCancelRequested = MakeShared(); FTimelineStatistics::CreateAggregation(Timelines, BucketMapper, IntervalStart, IntervalEnd, bIsCancelRequested, Aggregation); TTable* Table = new TTable(AggregatedStatsTableLayout); for (const auto& KV : Aggregation) { const FPackageInfo* Package = KV.Key; if (!Package) { continue; } FLoadTimeProfilerAggregatedStats& Row = Table->AddRow(); Row.Name = Package->Name; const FAggregatedTimingStats& Stats = KV.Value; Row.Count = Stats.InstanceCount; Row.Total = Stats.TotalExclusiveTime; Row.Min = Stats.MinExclusiveTime; Row.Max = Stats.MaxExclusiveTime; Row.Average = Stats.AverageExclusiveTime; Row.Median = Stats.MedianExclusiveTime; } return Table; } ITable* FLoadTimeProfilerProvider::CreateObjectTypeAggregation(double IntervalStart, double IntervalEnd) const { TArray Timelines; for (const TSharedRef& Timeline : CpuTimelines) { Timelines.Add(&Timeline.Get()); } auto BucketMapper = [](const FLoadTimeProfilerCpuEvent& Event) -> const FClassInfo* { return Event.Export ? Event.Export->Class : nullptr; }; TMap Aggregation; // Cancellation not currently implemented for LoadTimeProfilerProvider. TSharedPtr CancellationToken = MakeShared(); FTimelineStatistics::CreateAggregation(Timelines, BucketMapper, IntervalStart, IntervalEnd, CancellationToken, Aggregation); TTable* Table = new TTable(AggregatedStatsTableLayout); for (const auto& KV : Aggregation) { const FClassInfo* ClassInfo = KV.Key; if (ClassInfo) { FLoadTimeProfilerAggregatedStats& Row = Table->AddRow(); const FAggregatedTimingStats& Stats = KV.Value; Row.Name = ClassInfo->Name; Row.Count = Stats.InstanceCount; Row.Total = Stats.TotalExclusiveTime; Row.Min = Stats.MinExclusiveTime; Row.Max = Stats.MaxExclusiveTime; Row.Average = Stats.AverageExclusiveTime; Row.Median = Stats.MedianExclusiveTime; } } return Table; } ITable* FLoadTimeProfilerProvider::CreatePackageDetailsTable(double IntervalStart, double IntervalEnd) const { struct FTempRow { double StartTime = 0.0; const FPackageInfo* PackageInfo = nullptr; uint64 SerializedHeaderSize = 0; uint64 SerializedExportsCount = 0; uint64 SerializedExportsSize = 0; double MainThreadTime = 0.0; double AsyncLoadingThreadTime = 0.0; }; TArray Rows; struct FStackEntry { double StartTime; int32 RowIndex; }; TArray> Stack; double LastTime = 0.0; int32 TimelineIndex = 0; TMap RowMap; for (const TSharedRef& Timeline : CpuTimelines) { Timeline->EnumerateEvents(IntervalStart, IntervalEnd, [&Stack, &Rows, &RowMap, IntervalStart, IntervalEnd, &LastTime, TimelineIndex](bool IsEnter, double Time, const FLoadTimeProfilerCpuEvent& Event) { if (!Event.Package) { return EEventEnumerate::Continue; } double ClampedTime = FMath::Clamp(Time, IntervalStart, IntervalEnd); if (Stack.Num()) { FStackEntry& StackEntry = Stack.Top(); FTempRow& Row = Rows[StackEntry.RowIndex]; if (TimelineIndex == 0) { Row.MainThreadTime += ClampedTime - LastTime; } else { Row.AsyncLoadingThreadTime += ClampedTime - LastTime; } } LastTime = ClampedTime; if (IsEnter) { FTempRow* Row; int32 RowIndex = -1; int32* FindRowIndex = RowMap.Find(Event.Package); if (FindRowIndex) { Row = &Rows[*FindRowIndex]; RowIndex = *FindRowIndex; } else { RowIndex = Rows.Num(); Row = &Rows.AddDefaulted_GetRef(); Row->StartTime = Time; Row->PackageInfo = Event.Package; RowMap.Add(Event.Package, RowIndex); } FStackEntry& StackEntry = Stack.AddDefaulted_GetRef(); StackEntry.StartTime = ClampedTime; StackEntry.RowIndex = RowIndex; if (Event.Export && Event.EventType == LoadTimeProfilerObjectEventType_Serialize) { Row->SerializedExportsSize += Event.Export->SerialSize; ++Row->SerializedExportsCount; } else if (!Event.Export && Event.Package && !Row->SerializedHeaderSize) { Row->SerializedHeaderSize = Event.Package->Summary.TotalHeaderSize; } } else { Stack.Pop(EAllowShrinking::No); } return EEventEnumerate::Continue; }); ++TimelineIndex; } Algo::Sort(Rows, [](const FTempRow& A, const FTempRow& B) { return A.StartTime < B.StartTime; }); TTable* Table = new TTable(PackagesTableLayout); for (const FTempRow& TempRow : Rows) { FPackagesTableRow& Row = Table->AddRow(); Row.PackageInfo = TempRow.PackageInfo; Row.TotalSerializedSize = TempRow.SerializedHeaderSize + TempRow.SerializedExportsSize; Row.SerializedHeaderSize = TempRow.SerializedHeaderSize; Row.SerializedExportsCount = TempRow.SerializedExportsCount; Row.SerializedExportsSize = TempRow.SerializedExportsSize; Row.MainThreadTime = TempRow.MainThreadTime; Row.AsyncLoadingThreadTime = TempRow.AsyncLoadingThreadTime; } return Table; } ITable* FLoadTimeProfilerProvider::CreateExportDetailsTable(double IntervalStart, double IntervalEnd) const { struct FTempRow { double StartTime = 0.0; const FPackageExportInfo* ExportInfo = nullptr; uint64 SerializedSize = 0; double MainThreadTime = 0.0; double AsyncLoadingThreadTime = 0.0; ELoadTimeProfilerObjectEventType EventType; }; TArray Rows; struct FStackEntry { double StartTime; int32 RowIndex; }; TArray> Stack; double LastTime = 0.0; int32 TimelineIndex = 0; for (const TSharedRef& Timeline : CpuTimelines) { Timeline->EnumerateEvents(IntervalStart, IntervalEnd, [&Stack, &Rows, IntervalStart, IntervalEnd, &LastTime, TimelineIndex](bool IsEnter, double Time, const FLoadTimeProfilerCpuEvent& Event) { if (!Event.Export) { return EEventEnumerate::Continue; } double ClampedTime = FMath::Clamp(Time, IntervalStart, IntervalEnd); if (Stack.Num()) { FStackEntry& StackEntry = Stack.Top(); FTempRow& Row = Rows[StackEntry.RowIndex]; if (TimelineIndex == 0) { Row.MainThreadTime += ClampedTime - LastTime; } else { Row.AsyncLoadingThreadTime += ClampedTime - LastTime; } } LastTime = ClampedTime; if (IsEnter) { FStackEntry& StackEntry = Stack.AddDefaulted_GetRef(); StackEntry.StartTime = ClampedTime; StackEntry.RowIndex = Rows.Num(); FTempRow& Row = Rows.AddDefaulted_GetRef(); Row.StartTime = Time; Row.ExportInfo = Event.Export; Row.EventType = Event.EventType; if ( Event.EventType == LoadTimeProfilerObjectEventType_Serialize) { Row.SerializedSize = Event.Export->SerialSize; } } else { Stack.Pop(EAllowShrinking::No); } return EEventEnumerate::Continue; }); ++TimelineIndex; } Algo::Sort(Rows, [](const FTempRow& A, const FTempRow& B) { return A.StartTime < B.StartTime; }); TTable* Table = new TTable(ExportsTableLayout); for (const FTempRow& TempRow : Rows) { FExportsTableRow& Row = Table->AddRow(); Row.ExportInfo = TempRow.ExportInfo; Row.SerializedSize = TempRow.SerializedSize; Row.MainThreadTime = TempRow.MainThreadTime; Row.AsyncLoadingThreadTime = TempRow.AsyncLoadingThreadTime; Row.EventType = TempRow.EventType; } return Table; } ITable* FLoadTimeProfilerProvider::CreateRequestsTable(double IntervalStart, double IntervalEnd) const { TTable* Table = new TTable(RequestsTableLayout); for (const FLoadRequest& Request : Requests) { if (Request.EndTime < IntervalStart || Request.StartTime > IntervalEnd) { continue; } FRequestsTableRow& Row = Table->AddRow(); Row.Id = Request.Id; Row.Name = Request.Name; Row.StartTime = Request.StartTime; Row.Duration = Request.EndTime - Request.StartTime; Row.Packages = Request.Packages; } return Table; } const FClassInfo& FLoadTimeProfilerProvider::AddClassInfo(const TCHAR* ClassName) { Session.WriteAccessCheck(); FClassInfo& ClassInfo = ClassInfos.PushBack(); ClassInfo.Name = Session.StoreString(ClassName); return ClassInfo; } FLoadRequest& FLoadTimeProfilerProvider::CreateRequest() { Session.WriteAccessCheck(); FLoadRequest& RequestInfo = Requests.PushBack(); RequestInfo.Id = Requests.Num(); return RequestInfo; } FPackageInfo& FLoadTimeProfilerProvider::CreatePackage() { Session.WriteAccessCheck(); uint32 PackageId = uint32(Packages.Num()); FPackageInfo& Package = Packages.PushBack(); Package.Id = PackageId; return Package; } void FLoadTimeProfilerProvider::DistributeBytesAcrossFrames(uint64 ByteCount, double StartTime, double EndTime, uint64 FLoaderFrame::* FrameVariable) { uint64 OriginalByteCount = ByteCount; int32 BeginFrameIndex = int32(FMath::RoundToZero(StartTime / LoaderFrameLength)); int32 EndFrameIndex = int32(FMath::RoundToZero(EndTime / LoaderFrameLength)); while (Frames.Num() <= EndFrameIndex + 1) { Frames.PushBack(); } if (BeginFrameIndex == EndFrameIndex) { FLoaderFrame& Frame = Frames[BeginFrameIndex]; Frame.*FrameVariable += ByteCount; return; } double TotalTime = EndTime - StartTime; double TimeInFirstFrame = (BeginFrameIndex + 1) * LoaderFrameLength - StartTime; check(TimeInFirstFrame >= 0.0); uint64 BytesInFirstFrame = uint64(FMath::RoundToZero(TimeInFirstFrame / TotalTime * double(ByteCount))); Frames[BeginFrameIndex].*FrameVariable += BytesInFirstFrame; uint64 TotalDistributed = BytesInFirstFrame; double TimeInLastFrame = EndTime - EndFrameIndex * LoaderFrameLength; check(TimeInLastFrame >= 0.0); uint64 BytesInLastFrame = uint64(FMath::RoundToZero(TimeInLastFrame / TotalTime * double(ByteCount))); check(BytesInFirstFrame + BytesInLastFrame <= ByteCount); ByteCount -= BytesInFirstFrame + BytesInLastFrame; int32 FullFramesLeft = EndFrameIndex - BeginFrameIndex - 1; for (int32 FrameIndex = BeginFrameIndex + 1; FrameIndex < EndFrameIndex; ++FrameIndex) { uint64 BytesInFrame = ByteCount / FullFramesLeft; Frames[FrameIndex].*FrameVariable += BytesInFrame; TotalDistributed += BytesInFrame; ByteCount -= BytesInFrame; --FullFramesLeft; } BytesInLastFrame += ByteCount; Frames[EndFrameIndex].*FrameVariable += BytesInLastFrame; TotalDistributed += BytesInLastFrame; check(TotalDistributed == OriginalByteCount); } uint64 FLoadTimeProfilerProvider::BeginIoDispatcherBatch(uint64 BatchId, double Time) { Session.WriteAccessCheck(); CreateCounters(); ActiveIoDispatcherBatchesCounter->AddValue(Time, int64(1)); uint64 BatchHandle = IoDispatcherBatches.Num(); FIoDispatcherBatch& Batch = IoDispatcherBatches.PushBack(); Batch.StartTime = Time; return BatchHandle; } void FLoadTimeProfilerProvider::EndIoDispatcherBatch(uint64 BatchHandle, double Time, uint64 TotalSize) { Session.WriteAccessCheck(); FIoDispatcherBatch& Batch = IoDispatcherBatches[BatchHandle]; Batch.EndTime = Time; Batch.TotalSize = TotalSize; check(Batch.EndTime >= Batch.StartTime); DistributeBytesAcrossFrames(Batch.TotalSize, Batch.StartTime, Batch.EndTime, &FLoaderFrame::IoDispatcherReadBytes); TotalIoDispatcherBytesReadCounter->AddValue(Time, int64(Batch.TotalSize)); ActiveIoDispatcherBatchesCounter->AddValue(Time, int64(-1)); } FPackageExportInfo& FLoadTimeProfilerProvider::CreateExport() { Session.WriteAccessCheck(); uint32 ExportId = uint32(Exports.Num()); FPackageExportInfo& Export = Exports.PushBack(); Export.Id = ExportId; return Export; } FLoadTimeProfilerProvider::CpuTimelineInternal& FLoadTimeProfilerProvider::EditCpuTimeline(uint32 ThreadId) { Session.WriteAccessCheck(); uint32* FindTimelineIndex = CpuTimelinesThreadMap.Find(ThreadId); if (FindTimelineIndex) { return CpuTimelines[*FindTimelineIndex].Get(); } else { TSharedRef Timeline = MakeShared(Session.GetLinearAllocator()); uint32 TimelineIndex = CpuTimelines.Num(); CpuTimelinesThreadMap.Add(ThreadId, TimelineIndex); CpuTimelines.Add(Timeline); return Timeline.Get(); } } uint64 FLoadTimeProfilerProvider::PackageSizeSum(const FLoadRequest& Row) { uint64 Sum = 0; for (const FPackageInfo* Package : Row.Packages) { Sum += Package->Summary.TotalHeaderSize; for (const FPackageExportInfo* Export : Package->Exports) { Sum += Export->SerialSize; } } return Sum; } void FLoadTimeProfilerProvider::CreateCounters() { if (!bHasCreatedCounters) { for (int32 CounterType = 0; CounterType < FLoaderFrameCounter::LoaderFrameCounterType_Count; ++CounterType) { EditableCounterProvider.AddCounter(new FLoaderFrameCounter(FLoaderFrameCounter::ELoaderFrameCounterType(CounterType), Frames)); } ActiveIoDispatcherBatchesCounter = EditableCounterProvider.CreateEditableCounter(); ActiveIoDispatcherBatchesCounter->SetName(TEXT("AssetLoading/IoDispatcher/ActiveBatches")); TotalIoDispatcherBytesReadCounter = EditableCounterProvider.CreateEditableCounter(); TotalIoDispatcherBytesReadCounter->SetName(TEXT("AssetLoading/IoDispatcher/TotalBytesRead")); TotalIoDispatcherBytesReadCounter->SetDisplayHint(CounterDisplayHint_Memory); LoadingPackagesCounter = EditableCounterProvider.CreateEditableCounter(); LoadingPackagesCounter->SetName(TEXT("AssetLoading/AsyncLoading/ActiveLoadingPackages")); TotalLoaderBytesLoadedCounter = EditableCounterProvider.CreateEditableCounter(); TotalLoaderBytesLoadedCounter->SetName(TEXT("AssetLoading/AsyncLoading/TotalBytesLoaded")); TotalLoaderBytesLoadedCounter->SetDisplayHint(CounterDisplayHint_Memory); bHasCreatedCounters = true; } } const TCHAR* GetLoadTimeProfilerObjectEventTypeString(ELoadTimeProfilerObjectEventType EventType) { switch (EventType) { case LoadTimeProfilerObjectEventType_Create: return TEXT("Create"); case LoadTimeProfilerObjectEventType_Serialize: return TEXT("Serialize"); case LoadTimeProfilerObjectEventType_PostLoad: return TEXT("PostLoad"); case LoadTimeProfilerObjectEventType_None: return TEXT("None"); } return TEXT("[invalid]"); } } // namespace TraceServices