// Copyright Epic Games, Inc. All Rights Reserved. #if WITH_STATETREE_DEBUGGER #include "Debugger/StateTreeTraceProvider.h" #include "StateTreeTypes.h" #include "Debugger/StateTreeDebugger.h" FName FStateTreeTraceProvider::ProviderName("StateTreeDebuggerProvider"); #define LOCTEXT_NAMESPACE "StateTreeDebuggerProvider" FStateTreeTraceProvider::FStateTreeTraceProvider(TraceServices::IAnalysisSession& InSession) : Session(InSession) { } bool FStateTreeTraceProvider::ReadTimelines(const FStateTreeInstanceDebugId InstanceId, const TFunctionRef Callback) const { Session.ReadAccessCheck(); // Read specific timeline if specified if (InstanceId.IsValid()) { const uint32* IndexPtr = InstanceIdToDebuggerEntryTimelines.Find(InstanceId); if (IndexPtr != nullptr && EventsTimelines.IsValidIndex(*IndexPtr)) { Callback(InstanceId, *EventsTimelines[*IndexPtr]); return true; } } else { for(auto It = InstanceIdToDebuggerEntryTimelines.CreateConstIterator(); It; ++It) { if (EventsTimelines.IsValidIndex(It.Value())) { Callback(It.Key(), *EventsTimelines[It.Value()]); } } return EventsTimelines.Num() > 0; } return false; } bool FStateTreeTraceProvider::ReadTimelines(const UStateTree& StateTree, TFunctionRef Callback) const { Session.ReadAccessCheck(); for (auto It = InstanceIdToDebuggerEntryTimelines.CreateConstIterator(); It; ++It) { check(EventsTimelines.IsValidIndex(It.Value())); check(Descriptors.Num() == EventsTimelines.Num()); if (Descriptors[It.Value()].StateTree == &StateTree) { Callback(Descriptors[It.Value()].Id, *EventsTimelines[It.Value()]); } } return EventsTimelines.Num() > 0; } void FStateTreeTraceProvider::AppendEvent(const FStateTreeInstanceDebugId InInstanceId, const double InTime, const FStateTreeTraceEventVariantType& InEvent) { Session.WriteAccessCheck(); // It is currently possible to receive events from an instance without receiving event `EStateTreeTraceEventType::Push` first // (i.e. traces were activated after the statetree instance execution was started). // We plan to buffer the Instance events (Started/Stopped) to address this but for now we ignore events related to that instance. if (const uint32* IndexPtr = InstanceIdToDebuggerEntryTimelines.Find(InInstanceId)) { EventsTimelines[*IndexPtr]->AppendEvent(InTime, InEvent); } Session.UpdateDurationSeconds(InTime); } void FStateTreeTraceProvider::AppendInstanceEvent( const UStateTree* InStateTree, const FStateTreeInstanceDebugId InInstanceId, const TCHAR* InInstanceName, const double InTime, const double InWorldRecordingTime, const EStateTreeTraceEventType InEventType) { if (InEventType == EStateTreeTraceEventType::Push) { Descriptors.Emplace(InStateTree, InInstanceId, InInstanceName, TRange(InWorldRecordingTime, std::numeric_limits::max())); check(InstanceIdToDebuggerEntryTimelines.Find(InInstanceId) == nullptr); InstanceIdToDebuggerEntryTimelines.Add(InInstanceId, EventsTimelines.Num()); EventsTimelines.Emplace(MakeShared>(Session.GetLinearAllocator())); } else if (InEventType == EStateTreeTraceEventType::Pop) { // Process only if timeline can be found. See details in AppendEvent comment. if (const uint32* Index = InstanceIdToDebuggerEntryTimelines.Find(InInstanceId)) { check(Descriptors.IsValidIndex(*Index)); Descriptors[*Index].Lifetime.SetUpperBound(InWorldRecordingTime); } } Session.UpdateDurationSeconds(InTime); } void FStateTreeTraceProvider::GetInstances(TArray& OutInstances) const { OutInstances = Descriptors; } #undef LOCTEXT_NAMESPACE #endif // WITH_STATETREE_DEBUGGER