// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "Delegates/Delegate.h" #include "Input/CursorReply.h" #include "Input/Reply.h" #include "Layout/Geometry.h" #include "Styling/SlateTypes.h" #include "TraceServices/AnalysisService.h" #include "Widgets/DeclarativeSyntaxSupport.h" #include "Widgets/SCompoundWidget.h" #include "Widgets/SWidget.h" // Insights #include "Insights/Common/FixedCircularBuffer.h" #include "Insights/ITimingViewSession.h" #include "Insights/ViewModels/BaseTimingTrack.h" #include "Insights/ViewModels/TimerNode.h" #include "Insights/ViewModels/TimingEvent.h" #include "Insights/ViewModels/TimingEventsTrack.h" #include "Insights/ViewModels/TimingTrackViewport.h" #include "Insights/ViewModels/TimingViewDrawHelper.h" #include "Insights/ViewModels/TooltipDrawState.h" class FFileActivitySharedState; class FFrameSharedState; class FLoadingSharedState; class FMarkersTimingTrack; class FMenuBuilder; class FThreadTimingSharedState; class FTimeRulerTrack; class FTimingGraphTrack; class FTimingViewDrawHelper; class SOverlay; class SScrollBar; namespace Insights { class ITimingViewExtender; class FTimeMarker; } // namespace Insights /** A custom widget used to display timing events. */ class STimingView: public SCompoundWidget , public Insights::ITimingViewSession { public: /** Default constructor. */ STimingView(); /** Virtual destructor. */ virtual ~STimingView(); SLATE_BEGIN_ARGS(STimingView) { _Clipping = EWidgetClipping::ClipToBounds; } SLATE_END_ARGS() /** * Construct this widget * * @param InArgs The declaration data for this widget */ void Construct(const FArguments& InArgs); TSharedRef MakeAutoScrollOptionsMenu(); TSharedRef MakeTracksFilterMenu(); void CreateAllTracksMenu(FMenuBuilder& MenuBuilder); bool ShowHideGraphTrack_IsChecked() const; void ShowHideGraphTrack_Execute(); bool IsAutoHideEmptyTracksEnabled() const; void ToggleAutoHideEmptyTracks(); bool ToggleTrackVisibility_IsChecked(uint64 InTrackId) const; void ToggleTrackVisibility_Execute(uint64 InTrackId); TSharedPtr GetFrameSharedState() const { return FrameSharedState; } TSharedPtr GetThreadTimingSharedState() const { return ThreadTimingSharedState; } TSharedPtr GetLoadingSharedState() const { return LoadingSharedState; } TSharedPtr GetFileActivitySharedState() const { return FileActivitySharedState; } bool IsAssetLoadingModeEnabled() const { return bAssetLoadingMode; } void EnableAssetLoadingMode() { bAssetLoadingMode = true; } void HideAllDefaultTracks(); /** Resets internal widget's data to the default one. */ void Reset(bool bIsFirstReset = false); /** * Ticks this widget. Override in derived classes, but always call the parent implementation. * * @param AllottedGeometry The space allotted for this widget * @param InCurrentTime Current absolute real time * @param InDeltaTime Real time passed since last tick */ virtual void Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) override; virtual int32 OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const override; /** * The system calls this method to notify the widget that a mouse button was pressed within it. This event is bubbled. * * @param MyGeometry The Geometry of the widget receiving the event * @param MouseEvent Information about the input event * * @return Whether the event was handled along with possible requests for the system to take action. */ virtual FReply OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; /** * The system calls this method to notify the widget that a mouse button was release within it. This event is bubbled. * * @param MyGeometry The Geometry of the widget receiving the event * @param MouseEvent Information about the input event * * @return Whether the event was handled along with possible requests for the system to take action. */ virtual FReply OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; /** * Called when a mouse button is double clicked. Override this in derived classes. * * @param InMyGeometry Widget geometry * @param InMouseEvent Mouse button event * * @return Returns whether the event was handled, along with other possible actions */ virtual FReply OnMouseButtonDoubleClick(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; /** * The system calls this method to notify the widget that a mouse moved within it. This event is bubbled. * * @param MyGeometry The Geometry of the widget receiving the event * @param MouseEvent Information about the input event * * @return Whether the event was handled along with possible requests for the system to take action. */ virtual FReply OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; /** * The system will use this event to notify a widget that the cursor has entered it. This event is NOT bubbled. * * @param MyGeometry The Geometry of the widget receiving the event * @param MouseEvent Information about the input event */ virtual void OnMouseEnter(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; /** * The system will use this event to notify a widget that the cursor has left it. This event is NOT bubbled. * * @param MouseEvent Information about the input event */ virtual void OnMouseLeave(const FPointerEvent& MouseEvent) override; /** * Called when the mouse wheel is spun. This event is bubbled. * * @param MouseEvent Mouse event * * @return Returns whether the event was handled, along with other possible actions */ virtual FReply OnMouseWheel(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; /** * Called when the user is dropping something onto a widget; terminates drag and drop. * * @param MyGeometry The geometry of the widget receiving the event. * @param DragDropEvent The drag and drop event. * * @return A reply that indicated whether this event was handled. */ virtual FReply OnDrop(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent) override; /** * Called during drag and drop when the drag enters a widget. * * Enter/Leave events in slate are meant as lightweight notifications. * So we do not want to capture mouse or set focus in response to these. * However, OnDragEnter must also support external APIs (e.g. OLE Drag/Drop) * Those require that we let them know whether we can handle the content * being dragged OnDragEnter. * * The concession is to return a can_handled/cannot_handle * boolean rather than a full FReply. * * @param MyGeometry The geometry of the widget receiving the event. * @param DragDropEvent The drag and drop event. * * @return A reply that indicated whether the contents of the DragDropEvent can potentially be processed by this widget. */ virtual void OnDragEnter(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent) override; /** * Called during drag and drop when the drag leaves a widget. * * @param DragDropEvent The drag and drop event. */ virtual void OnDragLeave(const FDragDropEvent& DragDropEvent) override; /** * Called during drag and drop when the the mouse is being dragged over a widget. * * @param MyGeometry The geometry of the widget receiving the event. * @param DragDropEvent The drag and drop event. * * @return A reply that indicated whether this event was handled. */ virtual FReply OnDragOver(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent) override; /** * Called when the system wants to know which cursor to display for this Widget. This event is bubbled. * * @return The cursor requested (can be None.) */ virtual FCursorReply OnCursorQuery(const FGeometry& MyGeometry, const FPointerEvent& CursorEvent) const override; virtual bool SupportsKeyboardFocus() const override { return true; } virtual FReply OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent) override; virtual FReply OnKeyUp(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent) override; //////////////////////////////////////////////////////////////////////////////////////////////////// // ITimingViewSession interface virtual void AddTopDockedTrack(TSharedPtr Track) override { AddTrack(Track, ETimingTrackLocation::TopDocked); } virtual bool RemoveTopDockedTrack(TSharedPtr Track) override { return RemoveTrack(Track); } virtual void AddBottomDockedTrack(TSharedPtr Track) override { AddTrack(Track, ETimingTrackLocation::BottomDocked); } virtual bool RemoveBottomDockedTrack(TSharedPtr Track) override { return RemoveTrack(Track); } virtual void AddScrollableTrack(TSharedPtr Track) override { AddTrack(Track, ETimingTrackLocation::Scrollable); } virtual bool RemoveScrollableTrack(TSharedPtr Track) override { return RemoveTrack(Track); } virtual void InvalidateScrollableTracksOrder() override; virtual void AddForegroundTrack(TSharedPtr Track) override { AddTrack(Track, ETimingTrackLocation::Foreground); } virtual bool RemoveForegroundTrack(TSharedPtr Track) override { return RemoveTrack(Track); } virtual void AddTrack(TSharedPtr Track, ETimingTrackLocation Location) override; virtual bool RemoveTrack(TSharedPtr Track) override; virtual TSharedPtr FindTrack(uint64 InTrackId) override; virtual double GetTimeMarker() const override; virtual void SetTimeMarker(double InTimeMarker) override; virtual void SetAndCenterOnTimeMarker(double InTimeMarker) override; virtual Insights::FSelectionChangedDelegate& OnSelectionChanged() override { return OnSelectionChangedDelegate; } virtual Insights::FTimeMarkerChangedDelegate& OnTimeMarkerChanged() override { return OnTimeMarkerChangedDelegate; } virtual Insights::FHoveredTrackChangedDelegate& OnHoveredTrackChanged() override { return OnHoveredTrackChangedDelegate; } virtual Insights::FHoveredEventChangedDelegate& OnHoveredEventChanged() override { return OnHoveredEventChangedDelegate; } virtual Insights::FSelectedTrackChangedDelegate& OnSelectedTrackChanged() override { return OnSelectedTrackChangedDelegate; } virtual Insights::FSelectedEventChangedDelegate& OnSelectedEventChanged() override { return OnSelectedEventChangedDelegate; } virtual void PreventThrottling() override; virtual void AddOverlayWidget(const TSharedRef& InWidget) override; //////////////////////////////////////////////////////////////////////////////////////////////////// static const TCHAR* GetLocationName(ETimingTrackLocation Location); const TArray>& GetTrackList(ETimingTrackLocation TrackLocation) const { static const TArray> EmptyTrackList; switch (TrackLocation) { case ETimingTrackLocation::Scrollable: return ScrollableTracks; case ETimingTrackLocation::TopDocked: return TopDockedTracks; case ETimingTrackLocation::BottomDocked: return BottomDockedTracks; case ETimingTrackLocation::Foreground: return ForegroundTracks; default: return EmptyTrackList; } } void UpdateScrollableTracksOrder(); int32 GetFirstScrollableTrackOrder() const; int32 GetLastScrollableTrackOrder() const; void HideAllScrollableTracks(); void OnTrackVisibilityChanged(); bool IsGpuTrackVisible() const; bool IsCpuTrackVisible(uint32 InThreadId) const; //////////////////////////////////////////////////////////////////////////////////////////////////// const FTimingTrackViewport& GetViewport() const { return Viewport; } const FVector2D& GetMousePosition() const { return MousePosition; } double GetSelectionStartTime() const { return SelectionStartTime; } double GetSelectionEndTime() const { return SelectionEndTime; } bool IsPanning() const { return bIsPanning; } bool IsSelecting() const { return bIsSelecting; } bool IsTimeSelected(double Time) const { return Time >= SelectionStartTime && Time < SelectionEndTime; } bool IsTimeSelectedInclusive(double Time) const { return Time >= SelectionStartTime && Time <= SelectionEndTime; } void ScrollAtPosY(float ScrollPosY); void ScrollAtTime(double StartTime); void CenterOnTimeInterval(double IntervalStartTime, double IntervalDuration); void BringIntoView(double StartTime, double EndTime); void SelectTimeInterval(double IntervalStartTime, double IntervalDuration); void SelectToTimeMarker(double InTimeMarker); // bool AreTimeMarkersVisible() { return MarkersTrack->IsVisible(); } void SetTimeMarkersVisible(bool bOnOff); // bool IsDrawOnlyBookmarksEnabled() { return MarkersTrack->IsBookmarksTrack(); } void SetDrawOnlyBookmarks(bool bOnOff); TSharedPtr GetMainTimingGraphTrack() { return GraphTrack; } const TSharedPtr GetHoveredTrack() const { return HoveredTrack; } const TSharedPtr GetHoveredEvent() const { return HoveredEvent; } const TSharedPtr GetSelectedTrack() const { return SelectedTrack; } const TSharedPtr GetSelectedEvent() const { return SelectedEvent; } const TSharedPtr GetEventFilter() const { return TimingEventFilter; } void SetEventFilter(const TSharedPtr InEventFilter); void ToggleEventFilterByEventType(const uint64 EventType); bool IsFilterByEventType(const uint64 EventType) const; const TSharedPtr GetTrackAt(float InPosX, float InPosY) const; protected: virtual FVector2D ComputeDesiredSize(float) const override { return FVector2D(16.0f, 16.0f); } void ShowContextMenu(const FPointerEvent& MouseEvent); void CreateTrackLocationMenu(FMenuBuilder& MenuBuilder, TSharedRef Track); void ChangeTrackLocation(TSharedRef Track, ETimingTrackLocation NewLocation); bool CanChangeTrackLocation(TSharedRef Track, ETimingTrackLocation NewLocation) const; /** Binds our UI commands to delegates. */ void BindCommands(); //////////////////////////////////////////////////////////////////////////////////////////////////// // Auto-Scroll void AutoScroll_OnCheckStateChanged(ECheckBoxState NewRadioState); ECheckBoxState AutoScroll_IsChecked() const; void AutoScrollFrameAligned_Execute(); bool AutoScrollFrameAligned_IsChecked() const; void AutoScrollFrameType_Execute(ETraceFrameType FrameType); bool AutoScrollFrameType_CanExecute(ETraceFrameType FrameType) const; bool AutoScrollFrameType_IsChecked(ETraceFrameType FrameType) const; void AutoScrollViewportOffset_Execute(double Percent); bool AutoScrollViewportOffset_IsChecked(double Percent) const; void AutoScrollDelay_Execute(double Delay); bool AutoScrollDelay_IsChecked(double Delay) const; //////////////////////////////////////////////////////////////////////////////////////////////////// void UpdatePositionForScrollableTracks(); double EnforceHorizontalScrollLimits(const double InStartTime); float EnforceVerticalScrollLimits(const float InScrollPosY); /** * Called when the user scrolls the horizontal scrollbar. * * @param ScrollOffset Scroll offset as a fraction between 0 and 1. */ void HorizontalScrollBar_OnUserScrolled(float ScrollOffset); void UpdateHorizontalScrollBar(); /** * Called when the user scrolls the vertical scrollbar. * * @param ScrollOffset Scroll offset as a fraction between 0 and 1. */ void VerticalScrollBar_OnUserScrolled(float ScrollOffset); void UpdateVerticalScrollBar(); //////////////////////////////////////////////////////////////////////////////////////////////////// void RaiseSelectionChanging(); void RaiseSelectionChanged(); void RaiseTimeMarkerChanging(); void RaiseTimeMarkerChanged(); void UpdateAggregatedStats(); void UpdateHoveredTimingEvent(float InMousePosX, float InMousePosY); void OnSelectedTimingEventChanged(); void SelectHoveredTimingTrack(); void SelectHoveredTimingEvent(); void SelectLeftTimingEvent(); void SelectRightTimingEvent(); void SelectUpTimingEvent(); void SelectDownTimingEvent(); void FrameSelection(); // Export selected event and its children to JSON file (all threads) void ExportSelectedEventToJSON(); // Export selected event and its children to JSON file (current track only) void ExportSelectedEventToJSON_CurrentTrack(); // Get the name of the selected event FString GetSelectedEventName() const; // Get all the plugin extenders we care about TArray GetExtenders() const; FReply AllowTracksToProcessOnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent); FReply AllowTracksToProcessOnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent); FReply AllowTracksToProcessOnMouseButtonDoubleClick(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent); protected: /** The track's viewport. Encapsulates info about position and scale. */ FTimingTrackViewport Viewport; //////////////////////////////////////////////////////////// /** All created tracks. * Maps track id to track pointer. */ TMap> AllTracks; TArray> TopDockedTracks; /**< tracks docked on top, in the order to be displayed (top to bottom) */ TArray> BottomDockedTracks; /**< tracks docked on bottom, in the order to be displayed (top to bottom) */ TArray> ScrollableTracks; /**< tracks in scrollable area, in the order to be displayed (top to bottom) */ TArray> ForegroundTracks; /**< tracks to draw over top/scrollable/bottom tracks (can use entire area), in the order to be displayed (back to front) */ /** Whether the order of scrollable tracks is dirty and list need to be re-sorted */ bool bScrollableTracksOrderIsDirty; //////////////////////////////////////////////////////////// // Shared state for Frame Thread tracks TSharedPtr FrameSharedState; // Shared state for Cpu/Gpu Thread tracks TSharedPtr ThreadTimingSharedState; // Shared state for Asset Loading tracks TSharedPtr LoadingSharedState; bool bAssetLoadingMode; // Shared state for File Activity (I/O) tracks TSharedPtr FileActivitySharedState; //////////////////////////////////////////////////////////// /** The time ruler track. It includes the custom time markers (ones user can drag with mouse). */ TSharedRef TimeRulerTrack; /** The default time marker (for backward compatibility). */ TSharedRef DefaultTimeMarker; /** The time markers track. It displayes fixed time markers based on bookmarks and log messages. */ TSharedRef MarkersTrack; /** A graph track for frame times and cpu/gpu timing graphs. */ TSharedPtr GraphTrack; //////////////////////////////////////////////////////////// /** The extension overlay containing external sub-widgets */ TSharedPtr ExtensionOverlay; /** Horizontal scroll bar, used for scrolling timing events' viewport. */ TSharedPtr HorizontalScrollBar; /** Vertical scroll bar, used for scrolling timing events' viewport. */ TSharedPtr VerticalScrollBar; //////////////////////////////////////////////////////////// /** The current mouse position. */ FVector2D MousePosition; /** Mouse position during the call on mouse button down. */ FVector2D MousePositionOnButtonDown; double ViewportStartTimeOnButtonDown; float ViewportScrollPosYOnButtonDown; /** Mouse position during the call on mouse button up. */ FVector2D MousePositionOnButtonUp; float LastScrollPosY; bool bIsLMB_Pressed; bool bIsRMB_Pressed; bool bIsSpaceBarKeyPressed; bool bIsDragging; //////////////////////////////////////////////////////////// // Auto-Scroll /** True if the viewport scrolls automatically. */ bool bAutoScroll; /** True, if auto-scroll should align center of viewport with start of a frame. */ bool bIsAutoScrollFrameAligned; /** Type of frame to align with (Game or Rendering), if bIsAutoScrollFrameAligned is enabled. */ ETraceFrameType AutoScrollFrameType; /** * Viewport offset while auto-scrolling, as percent of viewport width. * If positive, it offsets the viewport forward, allowing an empty space at the right side of the viewport (i.e. after end of session). * If negative, it offsets the viewport backward (i.e. end of session will be outside viewport). */ double AutoScrollViewportOffsetPercent; /** Minimum time between two auto-scroll updates, in [seconds]. */ double AutoScrollMinDelay; /** Timestamp of last auto-scroll update, in [cycle64]. */ uint64 LastAutoScrollTime; //////////////////////////////////////////////////////////// // Panning /** True, if the user is currently interactively panning the view (horizontally and/or vertically). */ bool bIsPanning; /** How to pan. */ enum class EPanningMode : uint8 { None = 0, Horizontal = 0x01, Vertical = 0x02, HorizontalAndVertical = Horizontal | Vertical, }; EPanningMode PanningMode; float OverscrollLeft; float OverscrollRight; float OverscrollTop; float OverscrollBottom; //////////////////////////////////////////////////////////// // Selection /** True, if the user is currently changing the selection. */ bool bIsSelecting; double SelectionStartTime; double SelectionEndTime; TSharedPtr HoveredTrack; TSharedPtr HoveredEvent; TSharedPtr SelectedTrack; TSharedPtr SelectedEvent; TSharedPtr TimingEventFilter; FTooltipDrawState Tooltip; enum class ESelectionType { None, TimeRange, TimingEvent }; ESelectionType LastSelectionType; /** Throttle flag, allowing tracks to control whether Slate throttle should take place */ bool bPreventThrottling; //////////////////////////////////////////////////////////// // Misc FGeometry ThisGeometry; const FSlateBrush* WhiteBrush; const FSlateFontInfo MainFont; bool bDrawTopSeparatorLine; bool bDrawBottomSeparatorLine; // Debug stats int32 NumUpdatedEvents; TFixedCircularBuffer PreUpdateTracksDurationHistory; TFixedCircularBuffer UpdateTracksDurationHistory; TFixedCircularBuffer PostUpdateTracksDurationHistory; TFixedCircularBuffer TickDurationHistory; mutable TFixedCircularBuffer PreDrawTracksDurationHistory; mutable TFixedCircularBuffer DrawTracksDurationHistory; mutable TFixedCircularBuffer PostDrawTracksDurationHistory; mutable TFixedCircularBuffer TotalDrawDurationHistory; mutable TFixedCircularBuffer OnPaintDeltaTimeHistory; mutable uint64 LastOnPaintTime; //////////////////////////////////////////////////////////// // Delegates Insights::FSelectionChangedDelegate OnSelectionChangedDelegate; Insights::FTimeMarkerChangedDelegate OnTimeMarkerChangedDelegate; Insights::FHoveredTrackChangedDelegate OnHoveredTrackChangedDelegate; Insights::FHoveredEventChangedDelegate OnHoveredEventChangedDelegate; Insights::FSelectedTrackChangedDelegate OnSelectedTrackChangedDelegate; Insights::FSelectedEventChangedDelegate OnSelectedEventChangedDelegate; };