Files
UnrealEngine/Engine/Source/Developer/TraceInsights/Public/Insights/ViewModels/BaseTimingTrack.h
2025-05-18 13:04:45 +08:00

390 lines
16 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreTypes.h"
#include "Containers/Array.h"
#include "Input/Events.h"
#include "Input/Reply.h"
#include "Misc/EnumClassFlags.h"
#include "Templates/SharedPointer.h"
// TraceInsightsCore
#include "InsightsCore/Common/SimpleRtti.h"
// TraceInsights
#include "Insights/Config.h"
#if UE_INSIGHTS_BACKWARD_COMPATIBILITY_UE54
#include "Insights/ITimingViewSession.h"
#endif
////////////////////////////////////////////////////////////////////////////////////////////////////
class FMenuBuilder;
struct FGeometry;
namespace UE::Insights { class FDrawContext; }
namespace UE::Insights { class FFilterConfigurator; }
class FTimingEventSearchParameters;
class FTimingTrackViewport;
class ITimingViewDrawHelper;
class FTooltipDrawState;
class ITimingEvent;
class ITimingEventRelation;
class ITimingEventFilter;
struct FTimingViewLayout;
////////////////////////////////////////////////////////////////////////////////////////////////////
enum class ETimingTrackLocation : uint32
{
None = 0,
Scrollable = (1 << 0),
TopDocked = (1 << 1),
BottomDocked = (1 << 2),
Foreground = (1 << 3),
All = Scrollable | TopDocked | BottomDocked | Foreground
};
ENUM_CLASS_FLAGS(ETimingTrackLocation);
////////////////////////////////////////////////////////////////////////////////////////////////////
struct FTimingTrackOrder
{
static constexpr int32 GroupRange = 100000;
static constexpr int32 TimeRuler = -2 * GroupRange;
static constexpr int32 Markers = -1 * GroupRange;
static constexpr int32 First = 0;
static constexpr int32 Task = 1 * GroupRange;
static constexpr int32 Memory = 2 * GroupRange;
static constexpr int32 Gpu = 3 * GroupRange;
static constexpr int32 Cpu = 4 * GroupRange;
static constexpr int32 Last = 5 * GroupRange;
};
////////////////////////////////////////////////////////////////////////////////////////////////////
enum class ETimingTrackFlags : uint32
{
None = 0,
IsVisible = (1 << 0),
IsDirty = (1 << 1),
IsSelected = (1 << 2),
IsHovered = (1 << 3),
IsHeaderHovered = (1 << 4),
};
ENUM_CLASS_FLAGS(ETimingTrackFlags);
////////////////////////////////////////////////////////////////////////////////////////////////////
enum class EDrawEventMode : uint32
{
None = 0,
/** Draw the content of the event. This flag can be omitted in order to draw only the hovered/selected highlights. */
Content = (1 << 0),
/** Draw the highlights for a hovered event. */
Hovered = (1 << 1),
/** Draw the highlights for a selected event. */
Selected = (1 << 2),
/** Draw the highlights for an event that is both selected and hovered. */
SelectedAndHovered = Hovered | Selected,
};
ENUM_CLASS_FLAGS(EDrawEventMode);
////////////////////////////////////////////////////////////////////////////////////////////////////
class ITimingTrackUpdateContext
{
public:
virtual const FGeometry& GetGeometry() const = 0;
virtual const FTimingTrackViewport& GetViewport() const = 0;
virtual const FVector2D& GetMousePosition() const = 0;
virtual const TSharedPtr<const ITimingEvent> GetHoveredEvent() const = 0;
virtual const TSharedPtr<const ITimingEvent> GetSelectedEvent() const = 0;
virtual const TSharedPtr<ITimingEventFilter> GetEventFilter() const = 0;
virtual const TArray<TUniquePtr<ITimingEventRelation>>& GetCurrentRelations() const = 0;
virtual double GetCurrentTime() const = 0;
virtual float GetDeltaTime() const = 0;
};
////////////////////////////////////////////////////////////////////////////////////////////////////
class ITimingTrackDrawContext
{
public:
virtual const FTimingTrackViewport& GetViewport() const = 0;
virtual const FVector2D& GetMousePosition() const = 0;
virtual const TSharedPtr<const ITimingEvent> GetHoveredEvent() const = 0;
virtual const TSharedPtr<const ITimingEvent> GetSelectedEvent() const = 0;
virtual const TSharedPtr<ITimingEventFilter> GetEventFilter() const = 0;
virtual UE::Insights::FDrawContext& GetDrawContext() const = 0;
virtual const ITimingViewDrawHelper& GetHelper() const = 0;
};
////////////////////////////////////////////////////////////////////////////////////////////////////
class TRACEINSIGHTS_API FBaseTimingTrack : public TSharedFromThis<FBaseTimingTrack>
{
friend class FTimingViewDrawHelper;
INSIGHTS_DECLARE_RTTI_BASE(FBaseTimingTrack)
protected:
FBaseTimingTrack()
: Id(GenerateId())
{
}
explicit FBaseTimingTrack(const FString& InName)
: Id(GenerateId())
, Name(InName)
{
}
FBaseTimingTrack(const FBaseTimingTrack&) = delete;
virtual ~FBaseTimingTrack();
public:
virtual void Reset()
{
PosY = 0.0f;
Height = 0.0f;
Flags = ETimingTrackFlags::IsVisible | ETimingTrackFlags::IsDirty;
}
uint64 GetId() const { return Id; }
const FString& GetName() const { return Name; }
void SetName(const FString& InName) { Name = InName; }
ETimingTrackLocation GetValidLocations() const { return ValidLocations; }
ETimingTrackLocation GetLocation() const { return Location; }
void SetLocation(ETimingTrackLocation InLocation);
virtual void OnLocationChanged() { SetDirtyFlag(); }
int32 GetOrder() const { return Order; }
void SetOrder(int32 InOrder) { Order = InOrder; }
float GetPosY() const { return PosY; }
virtual void SetPosY(float InPosY) { PosY = InPosY; }
float GetHeight() const { return Height; }
virtual void SetHeight(float InHeight) { Height = InHeight; }
bool IsVisible() const { return EnumHasAnyFlags(Flags, ETimingTrackFlags::IsVisible); }
void Show() { Flags |= ETimingTrackFlags::IsVisible; OnVisibilityChanged(); }
void Hide() { Flags &= ~ETimingTrackFlags::IsVisible; OnVisibilityChanged(); }
void ToggleVisibility() { Flags ^= ETimingTrackFlags::IsVisible; OnVisibilityChanged(); }
void SetVisibilityFlag(bool bIsVisible) { bIsVisible ? Show() : Hide(); }
virtual void OnVisibilityChanged() { if (IsVisible()) SetDirtyFlag(); }
bool IsDirty() const { return EnumHasAnyFlags(Flags, ETimingTrackFlags::IsDirty); }
void SetDirtyFlag() { Flags |= ETimingTrackFlags::IsDirty; OnDirtyFlagChanged(); }
void ClearDirtyFlag() { Flags &= ~ETimingTrackFlags::IsDirty; OnDirtyFlagChanged(); }
virtual void OnDirtyFlagChanged() {}
bool IsSelected() const { return EnumHasAnyFlags(Flags, ETimingTrackFlags::IsSelected); }
void Select() { Flags |= ETimingTrackFlags::IsSelected; OnSelectedFlagChanged(); }
void Unselect() { Flags &= ~ETimingTrackFlags::IsSelected; OnSelectedFlagChanged(); }
void ToggleSelectedFlag() { Flags ^= ETimingTrackFlags::IsSelected; OnSelectedFlagChanged(); }
void SetSelectedFlag(bool bIsSelected) { bIsSelected ? Select() : Unselect(); }
virtual void OnSelectedFlagChanged() {}
bool IsHovered() const { return EnumHasAnyFlags(Flags, ETimingTrackFlags::IsHovered); }
void SetHoveredState(bool bIsHovered) { bIsHovered ? Flags |= ETimingTrackFlags::IsHovered : Flags &= ~ETimingTrackFlags::IsHovered; }
bool IsHeaderHovered() const { return EnumHasAllFlags(Flags, ETimingTrackFlags::IsHovered | ETimingTrackFlags::IsHeaderHovered); }
void SetHeaderHoveredState(bool bIsHeaderHovered) { bIsHeaderHovered ? Flags |= ETimingTrackFlags::IsHeaderHovered : Flags &= ~ETimingTrackFlags::IsHeaderHovered; }
//////////////////////////////////////////////////
// PreUpdate callback called each frame, but only if the track is visible.
// In this update, neither the position nor the size of the track is yet computed.
// Track should update here its height.
virtual void PreUpdate(const ITimingTrackUpdateContext& Context) {}
// Update callback called each frame, but only if the track is visible.
// In this update, it is assumed the track position and the track size are valid.
virtual void Update(const ITimingTrackUpdateContext& Context) {}
// PostUpdate callback called each frame, but only if the track is visible.
// Track should update here its "hovered" state.
virtual void PostUpdate(const ITimingTrackUpdateContext& Context) {}
//////////////////////////////////////////////////
// PreDraw callback (called from OnPaint) to draw something in the background.
virtual void PreDraw(const ITimingTrackDrawContext& Context) const {}
// Draw callback (called from OnPaint) to draw the track's content.
virtual void Draw(const ITimingTrackDrawContext& Context) const {}
// Draw a single event (can be used to draw only the highlight for a selected and/or hovered event).
virtual void DrawEvent(const ITimingTrackDrawContext& Context, const ITimingEvent& InTimingEvent, EDrawEventMode InDrawMode) const {}
// PostDraw callback (called from OnPaint) to draw something in the foreground.
virtual void PostDraw(const ITimingTrackDrawContext& Context) const {}
//////////////////////////////////////////////////
virtual FReply OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { return FReply::Unhandled(); }
virtual FReply OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { return FReply::Unhandled(); }
virtual FReply OnMouseButtonDoubleClick(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { return FReply::Unhandled(); }
virtual void BuildContextMenu(FMenuBuilder& MenuBuilder);
/**
* Gets the event at a specified position. Leaves the InOutEvent unchanged if no event is found at specified position.
* @param X The horizontal coordinate of the point tested; in Slate pixels (viewport coordinates).
* @param Y The vertical coordinate of the point tested; in Slate pixels (viewport coordinates).
* @param Viewport The timing viewport used to transform time in viewport coordinates.
* @return The event located at (PosX, PosY) coordinates, if any; nullptr otherwise.
*/
virtual const TSharedPtr<const ITimingEvent> GetEvent(float InPosX, float InPosY, const FTimingTrackViewport& Viewport) const { return nullptr; }
// Search for an event using custom parameters.
virtual const TSharedPtr<const ITimingEvent> SearchEvent(const FTimingEventSearchParameters& InSearchParameters) const { return nullptr; }
// Get the filter object for filtering all events similar with a specified event. Used when double clicked on an event.
virtual TSharedPtr<ITimingEventFilter> GetFilterByEvent(const TSharedPtr<const ITimingEvent> InTimingEvent) const { return nullptr; }
// Allows tracks to update event stats that are slower to compute (called at a lower frequency than GetEventAtPosition or Search or SearchTimingEvent).
virtual void UpdateEventStats(ITimingEvent& InOutEvent) const {}
// Called back from the timing view when an event is hovered by mouse.
virtual void OnEventHovered(const ITimingEvent& InHoveredEvent) const {}
// Called back from the timing view when an event is selected.
virtual void OnEventSelected(const ITimingEvent& InSelectedEvent) const {}
/**
* Called to initialize the tooltip's content with info from a timing event.
*
* @note In most cases, this should begin by calling `InOutTooltip.ResetContent()` before adding
* any content to the tooltip. However, in case the track may be used as a child track,
* the tooltip content should not be reset, and instead should only be appended to
* (because the parent track will reset the tooltip's content.)
* This is because other child tracks may want to append to the tooltip's content,
* and their appended content should not be reset.
*/
virtual void InitTooltip(FTooltipDrawState& InOutTooltip, const ITimingEvent& InTooltipEvent) const {}
// Called back from the timing view when an event is copied to the clipboard with Ctrl+C.
virtual void OnClipboardCopyEvent(const ITimingEvent& InSelectedEvent) const {}
// Adding children to tracks is a two-step process:
//
// 1. add the child track into the parent track using `AddChildTrack`
// 2. set the child track's parent track using `SetParentTrack`
//
// Both steps are needed to ensure the track renders properly.
// If a track is a child track, it must not also be added as a docked or scrollable track
// into the view.
//
// Note that child tracks do not respect the order established with `SetOrder`.
// Instead, you can reorder the elements individually in `GetChildTracks()`.
/** Gets a view into the array of child tracks, for enumeration and reordering. */
TArrayView<const TSharedRef<FBaseTimingTrack>> GetChildTracks() const { return ChildTracks; }
TArrayView<TSharedRef<FBaseTimingTrack>> GetChildTracks() { return ChildTracks; }
/**
* Adds a child track to this track.
* Note that this operation is idempotent. Calling this with the same track twice will only
* add the track once.
*
* @note This invalidates the result of `GetChildTracks`.
*/
void AddChildTrack(TSharedRef<FBaseTimingTrack> Track);
/**
* Inserts a child track to this track at the specified index.
* @note This invalidates the result of `GetChildTracks`.
*/
void AddChildTrack(TSharedRef<FBaseTimingTrack> Track, int32 Index);
/**
* Removes the provided track from this track's child tracks.
*
* @note This invalidates the result of `GetChildTracks`.
*/
void RemoveChildTrack(TSharedRef<FBaseTimingTrack> Track);
// The reason why FindChildTrackOfType does not consider the inheritance hierarchy is because
// if it did, downstream inheritors of specific public track types could break plugins defining
// them. Certain parts of Insights assume that there exists at least one instance of a child
// track of a given type (e.g. there must be at most one FContextSwitchesTimingTrack per
// FCpuCoreTimingTrack.)
/**
* Returns the first child track with the same type name.
* Note that the type name is matched exactly (without considering inheritance.)
* You should generally prefer the templated version over this.
*/
TSharedPtr<FBaseTimingTrack> FindChildTrackOfType(FName TrackTypeName);
/**
* Returns the first child track of the given type.
* Note that the type is matched exactly (without considering inheritance.)
* `T` must belong to the SimpleRTTI hierarchy.
*/
template<typename T>
TSharedPtr<T> FindChildTrackOfType()
{
return StaticCastSharedPtr<T>(FindChildTrackOfType(T::GetStaticTypeName()));
}
void SetParentTrack(TWeakPtr<FBaseTimingTrack> InTrack) { ParentTrack = InTrack; }
TWeakPtr<FBaseTimingTrack> GetParentTrack() const { return ParentTrack; }
bool IsChildTrack() const { return ParentTrack.IsValid(); }
float GetChildTracksTopHeight(const FTimingViewLayout& Layout) const;
void UpdateChildTracksPosY(const FTimingViewLayout& Layout);
// Legacy API supporting a single track. Do not use in new code.
UE_DEPRECATED(5.6, "Loop over GetChildTracks instead")
TSharedPtr<FBaseTimingTrack> GetChildTrack() const;
UE_DEPRECATED(5.6, "Use AddChildTrack/RemoveChildTrack instead")
void SetChildTrack(TSharedPtr<FBaseTimingTrack> InTrack);
virtual void SetFilterConfigurator(TSharedPtr<UE::Insights::FFilterConfigurator> InFilterConfigurator) {}
// Returns number of text lines needed to display the debug string.
//TODO: virtual int GetDebugStringLineCount() const { return 0; }
// Builds a string with debug text information.
//TODO: virtual void BuildDebugString(FString& OutStr) const {}
protected:
void SetValidLocations(ETimingTrackLocation InValidLocations) { ValidLocations = InValidLocations; }
static uint64 GenerateId() { return IdGenerator++; }
protected:
UE_DEPRECATED(5.6, "Use the ChildTracks array instead")
TSharedPtr<FBaseTimingTrack> ChildTrack;
TArray<TSharedRef<FBaseTimingTrack>> ChildTracks;
TWeakPtr<FBaseTimingTrack> ParentTrack;
private:
const uint64 Id;
FString Name;
ETimingTrackLocation ValidLocations = ETimingTrackLocation::Scrollable;
ETimingTrackLocation Location = ETimingTrackLocation::None;
int32 Order = 0;
float PosY = 0.0f; // y position, in Slate units
float Height = 0.0f; // height, in Slate units
ETimingTrackFlags Flags = ETimingTrackFlags::IsVisible | ETimingTrackFlags::IsDirty;
static uint64 IdGenerator;
};
////////////////////////////////////////////////////////////////////////////////////////////////////