3529 lines
114 KiB
C++
3529 lines
114 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "Widgets/SEventGraph.h"
|
|
|
|
#if STATS
|
|
|
|
#include "Widgets/Layout/SSplitter.h"
|
|
#include "Containers/MapBuilder.h"
|
|
#include "Widgets/SOverlay.h"
|
|
#include "SlateOptMacros.h"
|
|
#include "Framework/Application/SlateApplication.h"
|
|
#include "Widgets/Layout/SSeparator.h"
|
|
#include "Widgets/Images/SImage.h"
|
|
#include "Framework/MultiBox/MultiBoxBuilder.h"
|
|
#include "Widgets/Input/SButton.h"
|
|
#include "Widgets/Input/SComboBox.h"
|
|
#include "Widgets/Input/SComboButton.h"
|
|
#include "Widgets/Input/SCheckBox.h"
|
|
#include "Styling/AppStyle.h"
|
|
#include "Widgets/StatDragDropOp.h"
|
|
#include "Widgets/SEventGraphTooltip.h"
|
|
#include "Widgets/Input/SSearchBox.h"
|
|
#include "HAL/PlatformApplicationMisc.h"
|
|
#include "ProfilerStyle.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "SEventGraph"
|
|
|
|
|
|
namespace EEventGraphViewModes
|
|
{
|
|
FText ToName( const Type EventGraphViewMode )
|
|
{
|
|
switch( EventGraphViewMode )
|
|
{
|
|
case Hierarchical: return LOCTEXT("ViewMode_Name_Hierarchical", "Hierarchical");
|
|
case FlatInclusive: return LOCTEXT("ViewMode_Name_FlatInclusive", "Inclusive");
|
|
case FlatInclusiveCoalesced: return LOCTEXT("ViewMode_Name_FlatInclusiveCoalesced", "Inclusive");
|
|
case FlatExclusive: return LOCTEXT("ViewMode_Name_FlatExclusive", "Exclusive");
|
|
case FlatExclusiveCoalesced: return LOCTEXT("ViewMode_Name_FlatExclusiveCoalesced", "Exclusive" );
|
|
case ClassAggregate: return LOCTEXT("ViewMode_Name_ClassAggregate", "ClassAggregate");
|
|
|
|
default: return LOCTEXT("InvalidOrMax", "InvalidOrMax");
|
|
}
|
|
}
|
|
|
|
FText ToDescription( const Type EventGraphViewMode )
|
|
{
|
|
switch( EventGraphViewMode )
|
|
{
|
|
case Hierarchical: return LOCTEXT("ViewMode_Desc_Hierarchical", "Hierarchical tree view of the events");
|
|
case FlatInclusive: return LOCTEXT("ViewMode_Desc_Flat", "Flat list of the events, sorted by the inclusive time");
|
|
case FlatInclusiveCoalesced: return LOCTEXT("ViewMode_Desc_FlatCoalesced", "Flat list of the events coalesced by the event name, sorted by the inclusive time");
|
|
case FlatExclusive: return LOCTEXT("ViewMode_Desc_FlatExclusive", "Flat list of the events, sorted by the exclusive time");
|
|
case FlatExclusiveCoalesced: return LOCTEXT("ViewMode_Desc_FlatExclusiveCoalesced", "Flat list of the events coalesced by the event name, sorted by the exclusive time");
|
|
case ClassAggregate: return LOCTEXT("ViewMode_Desc_ClassAggregate", "ClassAggregate @TBD");
|
|
|
|
default: return LOCTEXT("InvalidOrMax", "InvalidOrMax");
|
|
}
|
|
}
|
|
|
|
FName ToBrushName( const Type EventGraphViewMode )
|
|
{
|
|
switch( EventGraphViewMode )
|
|
{
|
|
case Hierarchical: return TEXT("Profiler.EventGraph.HierarchicalIcon");
|
|
case FlatInclusive: return TEXT("Profiler.EventGraph.FlatIcon");
|
|
case FlatInclusiveCoalesced: return TEXT("Profiler.EventGraph.FlatCoalescedIcon");
|
|
case FlatExclusive: return TEXT("Profiler.EventGraph.FlatIcon");
|
|
case FlatExclusiveCoalesced: return TEXT("Profiler.EventGraph.FlatCoalescedIcon");
|
|
|
|
default: return NAME_None;
|
|
}
|
|
}
|
|
}
|
|
|
|
struct FEventGraphColumns
|
|
{
|
|
/** Default constructor. */
|
|
FEventGraphColumns()
|
|
{
|
|
// Make event property is initialized.
|
|
FEventGraphSample::InitializePropertyManagement();
|
|
|
|
Collection[(uint32)EEventPropertyIndex::StatName] = FEventGraphColumn
|
|
(
|
|
EEventPropertyIndex::StatName,
|
|
TEXT( "name" ),
|
|
LOCTEXT( "EventNameColumnTitle", "Event Name" ),
|
|
LOCTEXT( "EventNameColumnDesc", "Name of the event" ),
|
|
false, true, true, false, false,
|
|
HAlign_Left,
|
|
0.0f
|
|
);
|
|
|
|
Collection[(uint32)EEventPropertyIndex::InclusiveTimeMS] = FEventGraphColumn
|
|
(
|
|
EEventPropertyIndex::InclusiveTimeMS,
|
|
TEXT( "inc" ),
|
|
LOCTEXT( "InclusiveTimeMSTitle", "Inc Time (MS)" ),
|
|
LOCTEXT( "InclusiveTimeMSDesc", "Duration of the sample and its children, in milliseconds" ),
|
|
false, true, true, true, true,
|
|
HAlign_Right,
|
|
72.0f
|
|
);
|
|
|
|
Collection[(uint32)EEventPropertyIndex::InclusiveTimePct] = FEventGraphColumn
|
|
(
|
|
EEventPropertyIndex::InclusiveTimePct,
|
|
TEXT( "inc%" ),
|
|
LOCTEXT( "InclusiveTimePercentageTitle", "Inc Time (%)" ),
|
|
LOCTEXT( "InclusiveTimePercentageDesc", "Duration of the sample and its children as percent of the caller" ),
|
|
true, true, true, false, false,
|
|
HAlign_Right,
|
|
72.0f
|
|
);
|
|
|
|
Collection[(uint32)EEventPropertyIndex::ExclusiveTimeMS] = FEventGraphColumn
|
|
(
|
|
EEventPropertyIndex::ExclusiveTimeMS,
|
|
TEXT( "exc" ),
|
|
LOCTEXT( "ExclusiveTimeMSTitle", "Exc Time (MS)" ),
|
|
LOCTEXT( "ExclusiveTimeMSDesc", "Exclusive time of this event, in milliseconds" ),
|
|
false, true, true, true, false,
|
|
HAlign_Right,
|
|
72.0f
|
|
);
|
|
|
|
Collection[(uint32)EEventPropertyIndex::ExclusiveTimePct] = FEventGraphColumn
|
|
(
|
|
EEventPropertyIndex::ExclusiveTimePct,
|
|
TEXT( "exc%" ),
|
|
LOCTEXT( "ExclusiveTimePercentageTitle", "Exc Time (%)" ),
|
|
LOCTEXT( "ExclusiveTimePercentageDesc", "Exclusive time of this event as percent of this call's inclusive time" ),
|
|
true, true, true, false, false,
|
|
HAlign_Right,
|
|
72.0f
|
|
);
|
|
|
|
Collection[(uint32)EEventPropertyIndex::NumCallsPerFrame] = FEventGraphColumn
|
|
(
|
|
EEventPropertyIndex::NumCallsPerFrame,
|
|
TEXT( "calls" ),
|
|
LOCTEXT( "CallsPerFrameTitle", "Calls" ),
|
|
LOCTEXT( "CallsPerFrameDesc", "Number of times this event was called" ),
|
|
false, true, true, true, false,
|
|
HAlign_Right,
|
|
48.0f
|
|
);
|
|
|
|
// Fake column used as a default column for NAME_None
|
|
Collection[(uint32)EEventPropertyIndex::None] = FEventGraphColumn
|
|
(
|
|
EEventPropertyIndex::None,
|
|
TEXT( "None" ),
|
|
LOCTEXT( "None", "None" ),
|
|
LOCTEXT( "None", "None" ),
|
|
false, false, false, false, false,
|
|
HAlign_Left,
|
|
0.0f
|
|
);
|
|
|
|
ColumnNameToIndexMapping = TMapBuilder<FName, const FEventGraphColumn*>()
|
|
.Add( TEXT( "StatName" ), &Collection[(uint32)EEventPropertyIndex::StatName] )
|
|
.Add( TEXT( "InclusiveTimeMS" ), &Collection[(uint32)EEventPropertyIndex::InclusiveTimeMS] )
|
|
.Add( TEXT( "InclusiveTimePct" ), &Collection[(uint32)EEventPropertyIndex::InclusiveTimePct] )
|
|
.Add( TEXT( "ExclusiveTimeMS" ), &Collection[(uint32)EEventPropertyIndex::ExclusiveTimeMS] )
|
|
.Add( TEXT( "ExclusiveTimePct" ), &Collection[(uint32)EEventPropertyIndex::ExclusiveTimePct] )
|
|
.Add( TEXT( "NumCallsPerFrame" ), &Collection[(uint32)EEventPropertyIndex::NumCallsPerFrame] )
|
|
.Add( NAME_None, &Collection[(uint32)EEventPropertyIndex::None] )
|
|
;
|
|
}
|
|
|
|
static constexpr uint32 NumColumns = (uint32)EEventPropertyIndex::None + 1 ;
|
|
|
|
/** Contains basic information about columns used in the event graph widget. Names should be localized. */
|
|
FEventGraphColumn Collection[NumColumns];
|
|
|
|
// Generated from a XLSX file.
|
|
TMap<FName, const FEventGraphColumn*> ColumnNameToIndexMapping;
|
|
|
|
static const FEventGraphColumns& Get()
|
|
{
|
|
static FEventGraphColumns Instance;
|
|
return Instance;
|
|
}
|
|
};
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
SEventTreeItem
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
DECLARE_DELEGATE_TwoParams( FSetHoveredTableCell, const FName /*ColumnID*/, const FEventGraphSamplePtr /*SamplePtr*/ );
|
|
DECLARE_DELEGATE_RetVal_OneParam( bool, FIsColumnVisibleDelegate, const FName /*ColumnID*/ );
|
|
DECLARE_DELEGATE_RetVal_OneParam( EHorizontalAlignment, FGetColumnOutlineHAlignmentDelegate, const FName /*ColumnID*/ );
|
|
|
|
class SEventGraphTableCell : public SCompoundWidget
|
|
{
|
|
public:
|
|
SLATE_BEGIN_ARGS( SEventGraphTableCell ){}
|
|
SLATE_EVENT( FSetHoveredTableCell, OnSetHoveredTableCell )
|
|
SLATE_ARGUMENT( FEventGraphSamplePtr, EventPtr )
|
|
SLATE_ARGUMENT( FName, ColumnID )
|
|
SLATE_ARGUMENT( bool, IsEventNameColumn )
|
|
SLATE_END_ARGS()
|
|
|
|
/**
|
|
* Construct this widget.
|
|
*
|
|
* @param InArgs - the declaration data for this widget.
|
|
*/
|
|
void Construct( const FArguments& InArgs, const TSharedRef<class ITableRow>& TableRow, const TWeakPtr<IEventGraph>& InOwnerEventGraph )
|
|
{
|
|
SetHoveredTableCellDelegate = InArgs._OnSetHoveredTableCell;
|
|
EventPtr = InArgs._EventPtr;
|
|
OwnerEventGraph = InOwnerEventGraph;
|
|
ColumnID = InArgs._ColumnID;
|
|
|
|
ChildSlot
|
|
[
|
|
GenerateWidgetForColumnID( ColumnID, InArgs._IsEventNameColumn, TableRow )
|
|
];
|
|
}
|
|
|
|
protected:
|
|
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
|
TSharedRef<SWidget> GenerateWidgetForColumnID( const FName& InColumnID, const bool bIsEventNameColumn, const TSharedRef<class ITableRow>& TableRow )
|
|
{
|
|
const FEventGraphColumn& Column = *FEventGraphColumns::Get().ColumnNameToIndexMapping.FindChecked( InColumnID );
|
|
|
|
if( bIsEventNameColumn )
|
|
{
|
|
return
|
|
SNew(SHorizontalBox)
|
|
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.HAlign(HAlign_Right)
|
|
.VAlign(VAlign_Center)
|
|
[
|
|
SNew( SExpanderArrow, TableRow )
|
|
]
|
|
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.HAlign(HAlign_Center)
|
|
.VAlign(VAlign_Center)
|
|
[
|
|
SNew( SImage )
|
|
.Visibility( this, &SEventGraphTableCell::GetHotPathIconVisibility )
|
|
.Image(FProfilerStyle::Get().GetBrush(TEXT("Profiler.EventGraph.HotPathSmall")) )
|
|
]
|
|
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.VAlign( VAlign_Center )
|
|
.HAlign( Column.HorizontalAlignment )
|
|
.Padding( FMargin( 2.0f, 0.0f ) )
|
|
[
|
|
SNew( STextBlock )
|
|
.Text( FText::FromName(EventPtr->_StatName) )
|
|
.TextStyle(FProfilerStyle::Get(), TEXT("Profiler.Tooltip") )
|
|
.ColorAndOpacity( this, &SEventGraphTableCell::GetColorAndOpacity )
|
|
.ShadowColorAndOpacity( this, &SEventGraphTableCell::GetShadowColorAndOpacity )
|
|
]
|
|
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.HAlign(HAlign_Center)
|
|
.VAlign(VAlign_Center)
|
|
[
|
|
SNew( SButton )
|
|
.ButtonStyle( FAppStyle::Get(), TEXT("HoverHintOnly") )
|
|
.ContentPadding( 0.0f )
|
|
.IsFocusable( false )
|
|
.OnClicked( this, &SEventGraphTableCell::ExpandCulledEvents_OnClicked )
|
|
[
|
|
SNew( SImage )
|
|
.Visibility( this, &SEventGraphTableCell::GetCulledEventsIconVisibility )
|
|
.Image(FProfilerStyle::Get().GetBrush("Profiler.EventGraph.HasCulledEventsSmall") )
|
|
.ToolTipText( LOCTEXT("HasCulledEvents_TT","This event contains culled children, if you want to see all children, please disable culling or use function details, or press this icon") )
|
|
]
|
|
]
|
|
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.HAlign(HAlign_Center)
|
|
.VAlign(VAlign_Center)
|
|
[
|
|
SNew( SImage )
|
|
.Visibility( this, &SEventGraphTableCell::GetHintIconVisibility )
|
|
.Image(FProfilerStyle::Get().GetBrush("Profiler.Tooltip.HintIcon10") )
|
|
.ToolTip( SEventGraphTooltip::GetTableCellTooltip( EventPtr ) )
|
|
];
|
|
}
|
|
else
|
|
{
|
|
const FString FormattedValue = EventPtr->GetFormattedValue( Column.Index );
|
|
|
|
return
|
|
|
|
SNew(SHorizontalBox)
|
|
|
|
+SHorizontalBox::Slot()
|
|
.FillWidth( 1.0f )
|
|
.VAlign( VAlign_Center )
|
|
.HAlign( Column.HorizontalAlignment )
|
|
.Padding( FMargin( 2.0f, 0.0f ) )
|
|
[
|
|
SNew( STextBlock )
|
|
.Text( FText::FromString(FormattedValue) )
|
|
.TextStyle(FProfilerStyle::Get(), TEXT("Profiler.Tooltip") )
|
|
.ColorAndOpacity( this, &SEventGraphTableCell::GetColorAndOpacity )
|
|
.ShadowColorAndOpacity( this, &SEventGraphTableCell::GetShadowColorAndOpacity )
|
|
];
|
|
}
|
|
}
|
|
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
|
|
|
/**
|
|
* 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
|
|
{
|
|
SCompoundWidget::OnMouseEnter( MyGeometry, MouseEvent );
|
|
SetHoveredTableCellDelegate.ExecuteIfBound( ColumnID, EventPtr );
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
{
|
|
SCompoundWidget::OnMouseLeave( MouseEvent );
|
|
SetHoveredTableCellDelegate.ExecuteIfBound( NAME_None, nullptr );
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
{
|
|
SCompoundWidget::OnDragEnter( MyGeometry, DragDropEvent );
|
|
SetHoveredTableCellDelegate.ExecuteIfBound( ColumnID, EventPtr );
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
{
|
|
SCompoundWidget::OnDragLeave( DragDropEvent );
|
|
SetHoveredTableCellDelegate.ExecuteIfBound( NAME_None, nullptr );
|
|
}
|
|
|
|
EVisibility GetHotPathIconVisibility() const
|
|
{
|
|
const bool bIsHotPathIconVisible = EventPtr->_bIsHotPath;
|
|
return bIsHotPathIconVisible ? EVisibility::Visible : EVisibility::Collapsed;
|
|
}
|
|
|
|
EVisibility GetHintIconVisibility() const
|
|
{
|
|
return IsHovered() ? EVisibility::Visible : EVisibility::Hidden;
|
|
}
|
|
|
|
EVisibility GetCulledEventsIconVisibility() const
|
|
{
|
|
return EventPtr->HasCulledChildren() ? EVisibility::Visible : EVisibility::Collapsed;
|
|
}
|
|
|
|
FSlateColor GetColorAndOpacity() const
|
|
{
|
|
const FLinearColor TextColor = EventPtr->_bIsFiltered ? FLinearColor(1.0f,1.0f,1.0f,0.5f) : FLinearColor::White;
|
|
return TextColor;
|
|
}
|
|
|
|
FLinearColor GetShadowColorAndOpacity() const
|
|
{
|
|
const FLinearColor ShadowColor = EventPtr->_bIsFiltered ? FLinearColor(0.f,0.f,0.f,0.25f) : FLinearColor(0.f,0.f,0.f,0.5f);
|
|
return ShadowColor;
|
|
}
|
|
|
|
FReply ExpandCulledEvents_OnClicked()
|
|
{
|
|
OwnerEventGraph.Pin()->ExpandCulledEvents( EventPtr );
|
|
return FReply::Handled();
|
|
}
|
|
|
|
protected:
|
|
FSetHoveredTableCell SetHoveredTableCellDelegate;
|
|
|
|
/** A shared pointer to the event graph sample. */
|
|
FEventGraphSamplePtr EventPtr;
|
|
|
|
/** The event graph that owns this event graph cell. */
|
|
TWeakPtr< IEventGraph > OwnerEventGraph;
|
|
|
|
/** The ID of the column where this event graph belongs. */
|
|
FName ColumnID;
|
|
};
|
|
|
|
|
|
|
|
/** Widget that represents a table row in the event graph widget. Generates widgets for each column on demand. */
|
|
class SEventGraphTableRow : public SMultiColumnTableRow< FEventGraphSamplePtr >
|
|
{
|
|
public:
|
|
SLATE_BEGIN_ARGS( SEventGraphTableRow ){}
|
|
SLATE_EVENT( FIsColumnVisibleDelegate, OnIsColumnVisible )
|
|
SLATE_EVENT( FSetHoveredTableCell, OnSetHoveredTableCell )
|
|
SLATE_EVENT( FGetColumnOutlineHAlignmentDelegate, OnGetColumnOutlineHAlignmentDelegate )
|
|
SLATE_ATTRIBUTE( FName, HighlightedEventName )
|
|
SLATE_ARGUMENT( FEventGraphSamplePtr, EventPtr )
|
|
SLATE_END_ARGS()
|
|
|
|
/**
|
|
* Construct this widget. Called by the SNew() Slate macro.
|
|
*
|
|
* @param InArgs - Declaration used by the SNew() macro to construct this widget.
|
|
* @param InOwnerTableView - The owner table into which this row is being placed.
|
|
*/
|
|
void Construct( const FArguments& InArgs, const TSharedRef<STableViewBase>& InOwnerTableView, const TSharedRef<IEventGraph>& InOwnerEventGraph )
|
|
{
|
|
IsColumnVisibleDelegate = InArgs._OnIsColumnVisible;
|
|
SetHoveredTableCellDelegate = InArgs._OnSetHoveredTableCell;
|
|
GetColumnOutlineHAlignmentDelegate = InArgs._OnGetColumnOutlineHAlignmentDelegate;
|
|
HighlightedEventName = InArgs._HighlightedEventName;
|
|
EventPtr = InArgs._EventPtr;
|
|
OwnerEventGraph = InOwnerEventGraph;
|
|
|
|
SMultiColumnTableRow< FEventGraphSamplePtr >::Construct( SMultiColumnTableRow< FEventGraphSamplePtr >::FArguments(), InOwnerTableView );
|
|
}
|
|
|
|
/**
|
|
* Users of SMultiColumnTableRow would usually some piece of data associated with it.
|
|
* The type of this data is ItemType; it's the stuff that your TableView (i.e. List or Tree) is visualizing.
|
|
* The ColumnID tells you which column of the TableView we need to make a widget for.
|
|
* Make a widget and return it.
|
|
*
|
|
* @param ColumnID A unique ID for a column in this TableView; see SHeaderRow::FColumn for more info.
|
|
*
|
|
* @return a widget to represent the contents of a cell in this row of a TableView.
|
|
*/
|
|
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
|
virtual TSharedRef<SWidget> GenerateWidgetForColumn( const FName& ColumnID ) override
|
|
{
|
|
return
|
|
|
|
SNew(SOverlay)
|
|
.Visibility( EVisibility::SelfHitTestInvisible )
|
|
|
|
+SOverlay::Slot()
|
|
.Padding( 0.0f )
|
|
[
|
|
SNew( SImage )
|
|
.Image(FProfilerStyle::Get().GetBrush("Brushes.White25") )
|
|
.ColorAndOpacity( this, &SEventGraphTableRow::GetBackgroundColorAndOpacity )
|
|
]
|
|
|
|
+SOverlay::Slot()
|
|
.Padding( 0.0f )
|
|
[
|
|
SNew( SImage )
|
|
.Image( this, &SEventGraphTableRow::GetOutlineBrush, ColumnID )
|
|
.ColorAndOpacity( this, &SEventGraphTableRow::GetOutlineColorAndOpacity )
|
|
]
|
|
|
|
+SOverlay::Slot()
|
|
[
|
|
SNew( SEventGraphTableCell, SharedThis(this), OwnerEventGraph )
|
|
.Visibility( this, &SEventGraphTableRow::IsColumnVisible, ColumnID )
|
|
.ColumnID( ColumnID )
|
|
.IsEventNameColumn( ColumnID == FEventGraphColumns::Get().Collection[(uint32)EEventPropertyIndex::StatName].ID )
|
|
.EventPtr( EventPtr )
|
|
.OnSetHoveredTableCell( this, &SEventGraphTableRow::OnSetHoveredTableCell )
|
|
];
|
|
}
|
|
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
|
|
|
/**
|
|
* Called when Slate detects that a widget started to be dragged.
|
|
* Usage:
|
|
* A widget can ask Slate to detect a drag.
|
|
* OnMouseDown() reply with FReply::Handled().DetectDrag( SharedThis(this) ).
|
|
* Slate will either send an OnDragDetected() event or do nothing.
|
|
* If the user releases a mouse button or leaves the widget before
|
|
* a drag is triggered (maybe user started at the very edge) then no event will be
|
|
* sent.
|
|
*
|
|
* @param InMyGeometry Widget geometry
|
|
* @param InMouseEvent MouseMove that triggered the drag
|
|
*
|
|
*/
|
|
virtual FReply OnDragDetected( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ) override
|
|
{
|
|
if( MouseEvent.IsMouseButtonDown( EKeys::LeftMouseButton ) )
|
|
{
|
|
return FReply::Handled().BeginDragDrop( FStatIDDragDropOp::NewSingle( EventPtr->_StatID, EventPtr->_StatName.GetPlainNameString() ) );
|
|
}
|
|
|
|
return SMultiColumnTableRow< FEventGraphSamplePtr >::OnDragDetected(MyGeometry,MouseEvent);
|
|
}
|
|
|
|
protected:
|
|
|
|
FSlateColor GetBackgroundColorAndOpacity() const
|
|
{
|
|
const FLinearColor ThreadColor(5.0f,0.0f,0.0f,1.0f);
|
|
const FLinearColor DefaultColor(0.0f,0.0f,0.0f,0.0f);
|
|
const float Alpha = static_cast<float>(EventPtr->_FramePct) * 0.01f;
|
|
const FLinearColor BackgroundColorAndOpacity = FMath::Lerp(DefaultColor,ThreadColor,Alpha);
|
|
return BackgroundColorAndOpacity;
|
|
}
|
|
|
|
FSlateColor GetOutlineColorAndOpacity() const
|
|
{
|
|
const FLinearColor NoColor(0.0f,0.0f,0.0f,0.0f);
|
|
const bool bShouldBeHighlighted = EventPtr->_StatName == HighlightedEventName.Get();
|
|
const FLinearColor OutlineColorAndOpacity = bShouldBeHighlighted ? FLinearColor(FColorList::SlateBlue) : NoColor;
|
|
return OutlineColorAndOpacity;
|
|
}
|
|
|
|
const FSlateBrush* GetOutlineBrush( const FName ColumnID ) const
|
|
{
|
|
EHorizontalAlignment Result = HAlign_Center;
|
|
if( IsColumnVisibleDelegate.IsBound() )
|
|
{
|
|
Result = GetColumnOutlineHAlignmentDelegate.Execute( ColumnID );
|
|
}
|
|
|
|
const FSlateBrush* Brush = nullptr;
|
|
if( Result == HAlign_Left )
|
|
{
|
|
Brush = FProfilerStyle::Get().GetBrush("Profiler.EventGraph.Border.L");
|
|
}
|
|
else if( Result == HAlign_Right )
|
|
{
|
|
Brush = FProfilerStyle::Get().GetBrush("Profiler.EventGraph.Border.R");
|
|
}
|
|
else
|
|
{
|
|
Brush = FProfilerStyle::Get().GetBrush("Profiler.EventGraph.Border.TB");
|
|
}
|
|
return Brush;
|
|
}
|
|
|
|
EVisibility IsColumnVisible( const FName ColumnID ) const
|
|
{
|
|
EVisibility Result = EVisibility::Collapsed;
|
|
|
|
if( IsColumnVisibleDelegate.IsBound() )
|
|
{
|
|
Result = IsColumnVisibleDelegate.Execute( ColumnID ) ? EVisibility::Visible : EVisibility::Collapsed;
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
void OnSetHoveredTableCell( const FName InColumnID, const FEventGraphSamplePtr InSamplePtr )
|
|
{
|
|
SetHoveredTableCellDelegate.ExecuteIfBound( InColumnID, InSamplePtr );
|
|
}
|
|
|
|
protected:
|
|
FIsColumnVisibleDelegate IsColumnVisibleDelegate;
|
|
FSetHoveredTableCell SetHoveredTableCellDelegate;
|
|
FGetColumnOutlineHAlignmentDelegate GetColumnOutlineHAlignmentDelegate;
|
|
|
|
/** Name of the event that should be drawn as highlighted. */
|
|
TAttribute<FName> HighlightedEventName;
|
|
|
|
/** A shared pointer to the event graph sample. */
|
|
FEventGraphSamplePtr EventPtr;
|
|
|
|
/** The event graph that owns this event graph row. */
|
|
TWeakPtr< IEventGraph > OwnerEventGraph;
|
|
};
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
SEventTree
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
SEventGraph::SEventGraph()
|
|
: CurrentStateIndex( 0 )
|
|
{}
|
|
|
|
|
|
SEventGraph::~SEventGraph()
|
|
{
|
|
// Remove ourselves from the profiler manager.
|
|
if( FProfilerManager::Get().IsValid() )
|
|
{
|
|
FProfilerManager::Get()->OnViewModeChanged().RemoveAll( this );
|
|
}
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
Event graph construction related functions
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
|
void SEventGraph::Construct( const FArguments& InArgs )
|
|
{
|
|
static TArray<FEventGraphSamplePtr> StaticEventArray;
|
|
|
|
SAssignNew(ExternalScrollbar, SScrollBar)
|
|
.AlwaysShowScrollbar(true);
|
|
|
|
ChildSlot
|
|
[
|
|
SNew(SSplitter)
|
|
.Orientation(Orient_Vertical)
|
|
|
|
+ SSplitter::Slot()
|
|
.Value(0.5f)
|
|
[
|
|
SNew(SVerticalBox)
|
|
|
|
+ SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
[
|
|
SNew(SBorder)
|
|
.BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
|
|
.Padding(2.0f)
|
|
[
|
|
SNew(SVerticalBox)
|
|
|
|
+ SVerticalBox::Slot()
|
|
[
|
|
SNew(SHorizontalBox)
|
|
|
|
+ SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
[
|
|
GetWidgetForEventGraphTypes()
|
|
]
|
|
|
|
+ SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.Padding(2.0f, 0.0f, 0.0f, 0.0f)
|
|
[
|
|
GetWidgetForEventGraphViewModes()
|
|
]
|
|
|
|
+ SHorizontalBox::Slot()
|
|
.FillWidth(1.0f)
|
|
.Padding(2.0f, 0.0f, 0.0f, 0.0f)
|
|
[
|
|
GetWidgetBoxForOptions()
|
|
]
|
|
]
|
|
|
|
+ SVerticalBox::Slot()
|
|
.Padding(0.0f, 2.0f, 0.0f, 0.0f)
|
|
[
|
|
SNew(SHorizontalBox)
|
|
|
|
+ SHorizontalBox::Slot()
|
|
.FillWidth(1.0f)
|
|
[
|
|
GetWidgetForThreadFilter()
|
|
]
|
|
]
|
|
]
|
|
]
|
|
|
|
// Function details view ( @see VS2012 profiler )
|
|
+ SVerticalBox::Slot()
|
|
.FillHeight(1.0f)
|
|
.Padding(0.0f, 2.0f, 0.0f, 0.0f)
|
|
[
|
|
SAssignNew(FunctionDetailsBox,SBox)
|
|
.HeightOverride(224.0f)
|
|
[
|
|
SNew(SBorder)
|
|
.BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
|
|
.Padding(2.0f)
|
|
.Clipping(EWidgetClipping::ClipToBounds)
|
|
[
|
|
SNew(SHorizontalBox)
|
|
|
|
// Calling Functions
|
|
+ SHorizontalBox::Slot()
|
|
.FillWidth(1.0f)
|
|
.Padding(2.0f)
|
|
[
|
|
GetVerticalBoxForFunctionDetails(VerticalBox_TopCalling, LOCTEXT("FunctionDetails_CallingFunctions","Calling Functions"))
|
|
]
|
|
|
|
|
|
// Current Function
|
|
+ SHorizontalBox::Slot()
|
|
.FillWidth(1.0f)
|
|
.Padding(2.0f)
|
|
[
|
|
GetVerticalBoxForCurrentFunction()
|
|
]
|
|
// Called Functions
|
|
+ SHorizontalBox::Slot()
|
|
.FillWidth(1.0f)
|
|
.Padding(2.0f)
|
|
[
|
|
GetVerticalBoxForFunctionDetails( VerticalBox_TopCalled, LOCTEXT("FunctionDetails_CalledFunctions","Called Functions") )
|
|
]
|
|
]
|
|
]
|
|
]
|
|
]
|
|
|
|
+ SSplitter::Slot()
|
|
.Value(0.5f)
|
|
[
|
|
SNew(SBorder)
|
|
.BorderImage( FAppStyle::GetBrush("ToolPanel.GroupBorder") )
|
|
.Padding(0.0f)
|
|
[
|
|
SNew(SHorizontalBox)
|
|
|
|
+SHorizontalBox::Slot()
|
|
.FillWidth(1.0f)
|
|
[
|
|
SNew(SHorizontalBox)
|
|
|
|
+ SHorizontalBox::Slot()
|
|
[
|
|
SAssignNew(TreeView_Base, STreeView<FEventGraphSamplePtr>)
|
|
.ExternalScrollbar(ExternalScrollbar)
|
|
.SelectionMode(ESelectionMode::Multi)
|
|
.TreeItemsSource(&StaticEventArray)
|
|
.OnGetChildren(this, &SEventGraph::EventGraph_OnGetChildren)
|
|
.OnGenerateRow(this, &SEventGraph::EventGraph_OnGenerateRow)
|
|
.OnSelectionChanged(this, &SEventGraph::EventGraph_OnSelectionChanged)
|
|
.OnContextMenuOpening(FOnContextMenuOpening::CreateSP(this, &SEventGraph::EventGraph_GetMenuContent))
|
|
.HeaderRow
|
|
(
|
|
SAssignNew(TreeViewHeaderRow,SHeaderRow)
|
|
.Visibility(EVisibility::Visible)
|
|
)
|
|
]
|
|
]
|
|
|
|
+ SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
[
|
|
SNew(SBox)
|
|
.WidthOverride(FOptionalSize(16.0f))
|
|
[
|
|
ExternalScrollbar.ToSharedRef()
|
|
]
|
|
]
|
|
]
|
|
]
|
|
];
|
|
|
|
InitializeAndShowHeaderColumns();
|
|
BindCommands();
|
|
|
|
FProfilerManager::Get()->OnViewModeChanged().AddSP( this, &SEventGraph::ProfilerManager_OnViewModeChanged );
|
|
}
|
|
|
|
TSharedRef<SWidget> SEventGraph::GetToggleButtonForEventGraphType( const EEventGraphTypes::Type EventGraphType, const FName BrushName )
|
|
{
|
|
TSharedRef<SHorizontalBox> ButtonContent = SNew(SHorizontalBox);
|
|
|
|
if( BrushName != NAME_None )
|
|
{
|
|
ButtonContent->AddSlot()
|
|
.AutoWidth()
|
|
.HAlign( HAlign_Center )
|
|
.VAlign( VAlign_Center )
|
|
[
|
|
SNew(SImage)
|
|
.Image(FProfilerStyle::Get().GetBrush( BrushName ) )
|
|
];
|
|
}
|
|
|
|
ButtonContent->AddSlot()
|
|
.HAlign( HAlign_Center )
|
|
.VAlign( VAlign_Center )
|
|
[
|
|
SNew( STextBlock )
|
|
.Text( FText::FromString(EEventGraphTypes::ToName( EventGraphType )) )
|
|
.ColorAndOpacity(FSlateColor::UseForeground())
|
|
];
|
|
|
|
return
|
|
SNew( SCheckBox )
|
|
.Style(FProfilerStyle::Get(), "ToggleButtonCheckbox")
|
|
.IsEnabled( this, &SEventGraph::EventGraphType_IsEnabled, EventGraphType )
|
|
.HAlign( HAlign_Center )
|
|
.Padding( 2.0f )
|
|
.OnCheckStateChanged( this, &SEventGraph::EventGraphType_OnCheckStateChanged, EventGraphType )
|
|
.IsChecked( this, &SEventGraph::EventGraphType_IsChecked, EventGraphType )
|
|
.ToolTipText( FText::FromString(EEventGraphTypes::ToDescription( EventGraphType )) )
|
|
[
|
|
ButtonContent
|
|
];
|
|
}
|
|
|
|
TSharedRef<SWidget> SEventGraph::GetToggleButtonForEventGraphViewMode( const EEventGraphViewModes::Type EventGraphViewMode )
|
|
{
|
|
return
|
|
SNew( SCheckBox )
|
|
.Style(FProfilerStyle::Get(), "ToggleButtonCheckbox")
|
|
.IsEnabled( this, &SEventGraph::EventGraph_IsEnabled )
|
|
.HAlign( HAlign_Center )
|
|
.Padding( 2.0f )
|
|
.OnCheckStateChanged( this, &SEventGraph::EventGraphViewMode_OnCheckStateChanged, EventGraphViewMode )
|
|
.IsChecked( this, &SEventGraph::EventGraphViewMode_IsChecked, EventGraphViewMode )
|
|
.ToolTipText( EEventGraphViewModes::ToDescription( EventGraphViewMode ) )
|
|
.Visibility( this, &SEventGraph::EventGraphViewMode_GetVisibility, EventGraphViewMode )
|
|
[
|
|
SNew(SHorizontalBox)
|
|
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.HAlign( HAlign_Center )
|
|
.VAlign( VAlign_Center )
|
|
[
|
|
SNew(SImage)
|
|
.Image(FProfilerStyle::Get().GetBrush( EEventGraphViewModes::ToBrushName( EventGraphViewMode ) ) )
|
|
]
|
|
|
|
+SHorizontalBox::Slot()
|
|
.HAlign( HAlign_Center )
|
|
.VAlign( VAlign_Center )
|
|
[
|
|
SNew( STextBlock )
|
|
.Text( EEventGraphViewModes::ToName( EventGraphViewMode ) )
|
|
.ColorAndOpacity(FSlateColor::UseForeground())
|
|
]
|
|
];
|
|
}
|
|
|
|
TSharedRef<SWidget> SEventGraph::GetWidgetForEventGraphTypes()
|
|
{
|
|
return
|
|
SNew( SBorder )
|
|
.BorderImage(FProfilerStyle::Get().GetBrush("Profiler.Group.16") )
|
|
.Padding(FMargin(2.0f, 0.0))
|
|
[
|
|
SNew( SHorizontalBox )
|
|
|
|
// EventGraph - Type
|
|
+SHorizontalBox::Slot()
|
|
.HAlign( HAlign_Center )
|
|
.VAlign( VAlign_Center )
|
|
.AutoWidth()
|
|
.Padding( 2.0f )
|
|
[
|
|
SNew(STextBlock)
|
|
.Text( LOCTEXT("Toolbar_Type","Type") )
|
|
.TextStyle(FProfilerStyle::Get(), TEXT("Profiler.CaptionBold") )
|
|
]
|
|
|
|
// One-frame
|
|
+SHorizontalBox::Slot()
|
|
.HAlign( HAlign_Center )
|
|
.VAlign( VAlign_Fill )
|
|
.AutoWidth()
|
|
.Padding( 2.0f )
|
|
[
|
|
GetToggleButtonForEventGraphType( EEventGraphTypes::OneFrame, NAME_None )
|
|
]
|
|
|
|
// Avg
|
|
+SHorizontalBox::Slot()
|
|
.HAlign( HAlign_Center )
|
|
.VAlign( VAlign_Fill )
|
|
.AutoWidth()
|
|
.Padding( 2.0f )
|
|
[
|
|
GetToggleButtonForEventGraphType( EEventGraphTypes::Average, TEXT("Profiler.EventGraph.AverageIcon") )
|
|
]
|
|
|
|
// Max
|
|
+SHorizontalBox::Slot()
|
|
.HAlign( HAlign_Center )
|
|
.VAlign( VAlign_Fill )
|
|
.AutoWidth()
|
|
.Padding( 2.0f )
|
|
[
|
|
GetToggleButtonForEventGraphType( EEventGraphTypes::Maximum, TEXT("Profiler.EventGraph.MaximumIcon") )
|
|
]
|
|
];
|
|
}
|
|
|
|
|
|
TSharedRef<SWidget> SEventGraph::GetWidgetForEventGraphViewModes()
|
|
{
|
|
return
|
|
SNew( SBorder )
|
|
.BorderImage(FProfilerStyle::Get().GetBrush("Profiler.Group.16") )
|
|
.Padding(FMargin(2.0f, 0.0))
|
|
[
|
|
SNew( SHorizontalBox )
|
|
|
|
// View mode - Type
|
|
+SHorizontalBox::Slot()
|
|
.HAlign( HAlign_Center )
|
|
.VAlign( VAlign_Center )
|
|
.AutoWidth()
|
|
.Padding( 2.0f )
|
|
[
|
|
SNew(STextBlock)
|
|
.Text( LOCTEXT("Toolbar_ViewMode","View mode") )
|
|
.TextStyle(FProfilerStyle::Get(), TEXT("Profiler.CaptionBold") )
|
|
]
|
|
|
|
// View mode - Hierarchical
|
|
+SHorizontalBox::Slot()
|
|
.HAlign( HAlign_Center )
|
|
.VAlign( VAlign_Fill )
|
|
.AutoWidth()
|
|
.Padding( 2.0f )
|
|
[
|
|
GetToggleButtonForEventGraphViewMode( EEventGraphViewModes::Hierarchical )
|
|
]
|
|
|
|
// View mode - Flat (Inclusive)
|
|
+SHorizontalBox::Slot()
|
|
.HAlign( HAlign_Center )
|
|
.VAlign( VAlign_Fill )
|
|
.AutoWidth()
|
|
.Padding( 2.0f )
|
|
[
|
|
GetToggleButtonForEventGraphViewMode( EEventGraphViewModes::FlatInclusive )
|
|
]
|
|
|
|
// View mode - Flat Coalesced (Inclusive)
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.HAlign( HAlign_Center )
|
|
.VAlign( VAlign_Fill )
|
|
.Padding( 2.0f )
|
|
[
|
|
GetToggleButtonForEventGraphViewMode( EEventGraphViewModes::FlatInclusiveCoalesced )
|
|
]
|
|
|
|
// View mode - Flat (Exclusive)
|
|
+SHorizontalBox::Slot()
|
|
.HAlign( HAlign_Center )
|
|
.VAlign( VAlign_Fill )
|
|
.AutoWidth()
|
|
.Padding( 1.0f )
|
|
[
|
|
GetToggleButtonForEventGraphViewMode( EEventGraphViewModes::FlatExclusive )
|
|
]
|
|
|
|
// View mode - Flat Coalesced (Exclusive)
|
|
+SHorizontalBox::Slot()
|
|
.HAlign( HAlign_Center )
|
|
.VAlign( VAlign_Fill )
|
|
.AutoWidth()
|
|
.Padding( 2.0f )
|
|
[
|
|
GetToggleButtonForEventGraphViewMode( EEventGraphViewModes::FlatExclusiveCoalesced )
|
|
]
|
|
];
|
|
}
|
|
|
|
TSharedRef<SWidget> SEventGraph::GetWidgetForThreadFilter()
|
|
{
|
|
return SNew(SBorder)
|
|
.BorderImage(FProfilerStyle::Get().GetBrush("Profiler.Group.16"))
|
|
.Padding(FMargin(2.0f, 0.0))
|
|
[
|
|
SNew(SHorizontalBox)
|
|
|
|
+SHorizontalBox::Slot()
|
|
.HAlign( HAlign_Center )
|
|
.VAlign( VAlign_Center )
|
|
.AutoWidth()
|
|
.Padding( 2.0f )
|
|
[
|
|
SNew ( STextBlock )
|
|
.Text( LOCTEXT( "Toolbar_Thread", "Thread" ) )
|
|
.TextStyle(FProfilerStyle::Get(), TEXT( "Profiler.CaptionBold" ) )
|
|
]
|
|
|
|
+SHorizontalBox::Slot()
|
|
.HAlign( HAlign_Center )
|
|
.VAlign( VAlign_Fill )
|
|
.AutoWidth()
|
|
.Padding( 2.0f )
|
|
[
|
|
SAssignNew( ThreadFilterComboBox, SComboBox<TSharedPtr<FName>> )
|
|
.ContentPadding( FMargin( 6.0f, 2.0f ) )
|
|
.OptionsSource( &ThreadNamesForCombo )
|
|
.OnSelectionChanged( this, &SEventGraph::OnThreadFilterChanged )
|
|
.OnGenerateWidget( this, &SEventGraph::OnGenerateWidgetForThreadFilter )
|
|
[
|
|
SNew( STextBlock )
|
|
.Text( this, &SEventGraph::GenerateTextForThreadFilter, FName( TEXT( "SelectedThreadName" ) ) )
|
|
]
|
|
]
|
|
];
|
|
}
|
|
|
|
void SEventGraph::FillThreadFilterOptions()
|
|
{
|
|
ThreadNamesForCombo.Empty();
|
|
|
|
// Allow None as an option
|
|
ThreadNamesForCombo.Add(MakeShareable(new FName()));
|
|
|
|
if ( EventGraphStatesHistory.Num() == 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
FEventGraphSamplePtr Root = GetCurrentState()->GetRoot();
|
|
if ( !Root.IsValid() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Add a thread filter entry for each root child
|
|
for ( const FEventGraphSamplePtr& Child : Root->GetChildren() )
|
|
{
|
|
ThreadNamesForCombo.Add( MakeShareable( new FName( Child->_ThreadName ) ) );
|
|
}
|
|
|
|
// Sort the thread names alphabetically
|
|
ThreadNamesForCombo.Sort([]( const TSharedPtr<FName> Lhs, const TSharedPtr<FName> Rhs )
|
|
{
|
|
return Lhs->IsNone() || ( !Rhs->IsNone() && Lhs->LexicalLess(*Rhs) );
|
|
});
|
|
|
|
// Refresh the combo box
|
|
if ( ThreadFilterComboBox.IsValid() )
|
|
{
|
|
ThreadFilterComboBox->RefreshOptions();
|
|
}
|
|
}
|
|
|
|
FText SEventGraph::GenerateTextForThreadFilter( FName ThreadName ) const
|
|
{
|
|
static const FName SelectedThreadName( TEXT( "SelectedThreadName" ) );
|
|
if ( ThreadName == SelectedThreadName )
|
|
{
|
|
if ( EventGraphStatesHistory.Num() > 0 )
|
|
{
|
|
ThreadName = GetCurrentState()->ThreadFilter;
|
|
}
|
|
else
|
|
{
|
|
ThreadName = NAME_None;
|
|
}
|
|
}
|
|
return FText::FromName( ThreadName );
|
|
}
|
|
|
|
void SEventGraph::OnThreadFilterChanged( TSharedPtr<FName> NewThread, ESelectInfo::Type SelectionType )
|
|
{
|
|
if ( NewThread.IsValid() )
|
|
{
|
|
GetCurrentState()->ThreadFilter = *NewThread;
|
|
RestoreEventGraphStateFrom( GetCurrentState() );
|
|
GetCurrentState()->GetRoot()->SetBooleanStateForAllChildren<EEventPropertyIndex::bNeedNotCulledChildrenUpdate>(true);
|
|
}
|
|
}
|
|
|
|
TSharedRef<SWidget> SEventGraph::OnGenerateWidgetForThreadFilter( TSharedPtr<FName> ThreadName ) const
|
|
{
|
|
return SNew( STextBlock )
|
|
.Text( GenerateTextForThreadFilter( ThreadName.IsValid() ? *ThreadName : NAME_None ) );
|
|
}
|
|
|
|
TSharedRef<SWidget> SEventGraph::GetWidgetBoxForOptions()
|
|
{
|
|
return
|
|
SNew( SBorder )
|
|
.BorderImage(FProfilerStyle::Get().GetBrush("Profiler.Group.16") )
|
|
.Padding( 0.0f )
|
|
[
|
|
SNew(SHorizontalBox)
|
|
|
|
// History Back
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.Padding( 1.0f )
|
|
[
|
|
SNew( SButton )
|
|
.OnClicked( this, &SEventGraph::HistoryBack_OnClicked )
|
|
.IsEnabled( this, &SEventGraph::HistoryBack_IsEnabled )
|
|
.ToolTipText( this, &SEventGraph::HistoryBack_GetToolTipText )
|
|
.HAlign( HAlign_Center )
|
|
.VAlign( VAlign_Center )
|
|
.ContentPadding( 2.0f )
|
|
[
|
|
SNew(SImage)
|
|
.Image(FProfilerStyle::Get().GetBrush("Profiler.EventGraph.HistoryBack") )
|
|
]
|
|
]
|
|
|
|
// History Forward
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.Padding( 1.0f )
|
|
[
|
|
SNew( SButton )
|
|
.OnClicked( this, &SEventGraph::HistoryForward_OnClicked )
|
|
.IsEnabled( this, &SEventGraph::HistoryForward_IsEnabled )
|
|
.ToolTipText( this, &SEventGraph::HistoryForward_GetToolTipText )
|
|
.HAlign( HAlign_Center )
|
|
.VAlign( VAlign_Center )
|
|
.ContentPadding( 2.0f )
|
|
[
|
|
SNew(SImage)
|
|
.Image(FProfilerStyle::Get().GetBrush("Profiler.EventGraph.HistoryForward") )
|
|
]
|
|
]
|
|
|
|
// History List
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.Padding( 1.0f )
|
|
[
|
|
SNew( SComboButton )
|
|
.IsEnabled( this, &SEventGraph::HistoryList_IsEnabled )
|
|
.ContentPadding( 0.0f )
|
|
.OnGetMenuContent( this, &SEventGraph::HistoryList_GetMenuContent )
|
|
]
|
|
|
|
// Expand hot path
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.Padding( 1.0f )
|
|
[
|
|
SNew( SButton )
|
|
.IsEnabled( this, &SEventGraph::ContextMenu_ExpandHotPath_CanExecute )
|
|
.ToolTipText( LOCTEXT("ContextMenu_Header_Expand_ExpandHotPath_Desc", "Expands hot path for the selected events, based on the inclusive time, also enables descending sorting by inclusive time") )
|
|
.HAlign( HAlign_Center )
|
|
.VAlign( VAlign_Center )
|
|
.OnClicked( this, &SEventGraph::ExpandHotPath_OnClicked )
|
|
[
|
|
SNew( SImage )
|
|
.Image(FProfilerStyle::Get().GetBrush(TEXT("Profiler.EventGraph.ExpandHotPath16")) )
|
|
]
|
|
]
|
|
|
|
// Highlight hot path
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.HAlign( HAlign_Center )
|
|
.VAlign( VAlign_Fill )
|
|
.Padding( 1.0f )
|
|
[
|
|
SNew( SCheckBox )
|
|
.Visibility( EVisibility::Collapsed )
|
|
.IsEnabled( false )
|
|
.OnCheckStateChanged( this, &SEventGraph::HighlightHotPath_OnCheckStateChanged )
|
|
[
|
|
SNew( STextBlock )
|
|
.Text( LOCTEXT("HighlightHotPathCheckboxLabel", "HP") )
|
|
]
|
|
]
|
|
|
|
// Configuration
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.HAlign( HAlign_Center )
|
|
.VAlign( VAlign_Fill )
|
|
.Padding( 1.0f )
|
|
[
|
|
SNew( SButton )
|
|
.Visibility( EVisibility::Collapsed )
|
|
.IsEnabled( false )
|
|
.HAlign( HAlign_Center )
|
|
.VAlign( VAlign_Center )
|
|
.Text( LOCTEXT("ConfigurationButtonLabel", "CF") )
|
|
]
|
|
|
|
// Export
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.HAlign( HAlign_Center )
|
|
.VAlign( VAlign_Fill )
|
|
.Padding( 1.0f )
|
|
[
|
|
SNew( SButton )
|
|
.Visibility( EVisibility::Collapsed )
|
|
.IsEnabled( false )
|
|
.HAlign( HAlign_Center )
|
|
.VAlign( VAlign_Center )
|
|
.Text( LOCTEXT("ExportButtonLabel", "EX") )
|
|
]
|
|
|
|
// Search box
|
|
+SHorizontalBox::Slot()
|
|
.FillWidth( 1.0f )
|
|
.HAlign( HAlign_Fill )
|
|
.VAlign( VAlign_Center )
|
|
.Padding( 1.0f )
|
|
[
|
|
SAssignNew( FilteringSearchBox, SSearchBox )
|
|
.HintText( LOCTEXT("FilteringSearchBox_HintText", "Search or filter event(s)") )
|
|
.OnTextChanged( this, &SEventGraph::FilteringSearchBox_OnTextChanged )
|
|
.OnTextCommitted( this, &SEventGraph::FilteringSearchBox_OnTextCommitted )
|
|
.IsEnabled( this, &SEventGraph::FilteringSearchBox_IsEnabled )
|
|
.ToolTipText( LOCTEXT("FilteringSearchBox_TT", "Type here to search or filter events") )
|
|
]
|
|
|
|
// Aggressive filtering
|
|
+ SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.HAlign(HAlign_Center)
|
|
.VAlign(VAlign_Fill)
|
|
.Padding(1.0f)
|
|
[
|
|
SNew(SCheckBox)
|
|
.Visibility(EVisibility::Visible)
|
|
.IsEnabled(true)
|
|
.OnCheckStateChanged(this, &SEventGraph::OnAggressiveFilteringToggled)
|
|
[
|
|
SNew(STextBlock)
|
|
.Text( LOCTEXT("AggressiveFilteringLabel", "AF") )
|
|
.ToolTipText(LOCTEXT("AggressiveFiltering_TT", "Toggle aggressive filtering"))
|
|
]
|
|
]
|
|
|
|
|
|
// Filtering help
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.HAlign( HAlign_Center )
|
|
.VAlign( VAlign_Fill )
|
|
.Padding( 1.0f )
|
|
[
|
|
SNew( SButton )
|
|
.Visibility( EVisibility::Collapsed )
|
|
.IsEnabled( false )
|
|
.HAlign( HAlign_Center )
|
|
.VAlign( VAlign_Center )
|
|
.Text( LOCTEXT("FilteringHelpButtonLabel", "?") )
|
|
]
|
|
];
|
|
}
|
|
|
|
TSharedRef<SVerticalBox> SEventGraph::GetVerticalBoxForFunctionDetails( TSharedPtr<SVerticalBox>& out_VerticalBoxTopFuncions, const FText& Caption )
|
|
{
|
|
return
|
|
SNew(SVerticalBox)
|
|
|
|
+SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
.HAlign( HAlign_Center )
|
|
.VAlign( VAlign_Center )
|
|
.Padding( 2.0f )
|
|
[
|
|
SNew(STextBlock)
|
|
.Text( Caption )
|
|
.TextStyle(FProfilerStyle::Get(), TEXT("Profiler.CaptionBold") )
|
|
]
|
|
|
|
+SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
.HAlign( HAlign_Fill )
|
|
.VAlign( VAlign_Center )
|
|
.Padding( 2.0f )
|
|
[
|
|
SNew(SSeparator)
|
|
.Orientation( Orient_Horizontal )
|
|
]
|
|
|
|
+SVerticalBox::Slot()
|
|
.FillHeight( 1.0f )
|
|
.HAlign( HAlign_Fill )
|
|
.VAlign( VAlign_Fill )
|
|
.Padding( 0.0f )
|
|
[
|
|
SAssignNew(out_VerticalBoxTopFuncions,SVerticalBox)
|
|
];
|
|
}
|
|
|
|
TSharedRef<SVerticalBox> SEventGraph::GetVerticalBoxForCurrentFunction()
|
|
{
|
|
return
|
|
SNew(SVerticalBox)
|
|
|
|
+SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
.HAlign( HAlign_Center )
|
|
.VAlign( VAlign_Center )
|
|
.Padding( 2.0f )
|
|
[
|
|
SNew(STextBlock)
|
|
.Text( LOCTEXT("FunctionDetails_CurrentFunction","Current Function") )
|
|
.TextStyle(FProfilerStyle::Get(), TEXT("Profiler.CaptionBold") )
|
|
]
|
|
|
|
+SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
.HAlign( HAlign_Fill )
|
|
.VAlign( VAlign_Center )
|
|
.Padding( 2.0f )
|
|
[
|
|
SNew(SSeparator)
|
|
.Orientation( Orient_Horizontal )
|
|
]
|
|
|
|
+SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
.HAlign( HAlign_Center )
|
|
.VAlign( VAlign_Center )
|
|
.Padding( 2.0f )
|
|
.Expose( CurrentFunctionDescSlot );
|
|
}
|
|
|
|
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
...
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
TSharedRef< ITableRow > SEventGraph::EventGraph_OnGenerateRow( FEventGraphSamplePtr EventPtr, const TSharedRef<STableViewBase>& OwnerTable )
|
|
{
|
|
TSharedRef< ITableRow > ReturnRow = SNew(SEventGraphTableRow, OwnerTable, SharedThis(this))
|
|
.OnIsColumnVisible( this, &SEventGraph::EventGraphTableRow_IsColumnVisible )
|
|
.OnSetHoveredTableCell( this, &SEventGraph::EventGraphTableRow_SetHoveredTableCell )
|
|
.OnGetColumnOutlineHAlignmentDelegate( this, &SEventGraph::EventGraphRow_GetColumnOutlineHAlignment )
|
|
.HighlightedEventName( this, &SEventGraph::EventGraphRow_GetHighlightedEventName )
|
|
.EventPtr( EventPtr )
|
|
;
|
|
|
|
return ReturnRow;
|
|
}
|
|
|
|
void SEventGraph::EventGraph_OnSelectionChanged( FEventGraphSamplePtr SelectedItem, ESelectInfo::Type SelectInfo )
|
|
{
|
|
if( SelectInfo != ESelectInfo::Direct )
|
|
{
|
|
UpdateFunctionDetails();
|
|
}
|
|
}
|
|
|
|
bool SEventGraph::EventGraphTableRow_IsColumnVisible( const FName ColumnID ) const
|
|
{
|
|
bool bResult = false;
|
|
const FEventGraphColumn& ColumnPtr = TreeViewHeaderColumns.FindChecked(ColumnID);
|
|
return ColumnPtr.bIsVisible;
|
|
}
|
|
|
|
void SEventGraph::EventGraphTableRow_SetHoveredTableCell( const FName ColumnID, const FEventGraphSamplePtr EventPtr )
|
|
{
|
|
HoveredColumnID = ColumnID;
|
|
|
|
const bool bIsAnyMenusVisible = FSlateApplication::Get().AnyMenusVisible();
|
|
if( !HasMouseCapture() && !bIsAnyMenusVisible )
|
|
{
|
|
HoveredSamplePtr = EventPtr;
|
|
}
|
|
|
|
#if DEBUG_PROFILER_PERFORMANCE
|
|
UE_LOG( Profiler, Log, TEXT("%s -> %s"), *HoveredColumnID.GetPlainNameString(), EventPtr.IsValid() ? *EventPtr->_StatName.GetPlainNameString() : TEXT("nullptr") );
|
|
#endif // DEBUG_PROFILER_PERFORMANCE
|
|
}
|
|
|
|
|
|
FName SEventGraph::EventGraphRow_GetHighlightedEventName() const
|
|
{
|
|
return HighlightedEventName;
|
|
}
|
|
|
|
|
|
EHorizontalAlignment SEventGraph::EventGraphRow_GetColumnOutlineHAlignment( const FName ColumnID ) const
|
|
{
|
|
const TIndirectArray<SHeaderRow::FColumn>& Columns = TreeViewHeaderRow->GetColumns();
|
|
const int32 LastColumnIdx = Columns.Num()-1;
|
|
|
|
// First column
|
|
if( Columns[0].ColumnId == ColumnID )
|
|
{
|
|
return HAlign_Left;
|
|
}
|
|
// Last column
|
|
else if( Columns[LastColumnIdx].ColumnId == ColumnID )
|
|
{
|
|
return HAlign_Right;
|
|
}
|
|
// Middle columns
|
|
{
|
|
return HAlign_Center;
|
|
}
|
|
}
|
|
|
|
void SEventGraph::EventGraph_OnGetChildren( FEventGraphSamplePtr InParent, TArray< FEventGraphSamplePtr >& out_Children )
|
|
{
|
|
if( GetCurrentStateViewMode() == EEventGraphViewModes::Hierarchical )
|
|
{
|
|
out_Children = InParent->GetNotCulledChildren();
|
|
}
|
|
}
|
|
|
|
void SEventGraph::TreeView_SetItemsExpansion_Recurrent( TArray<FEventGraphSamplePtr>& InEventPtrs, const bool bShouldExpand )
|
|
{
|
|
for( int32 EventIndex = 0; EventIndex < InEventPtrs.Num(); EventIndex++ )
|
|
{
|
|
const FEventGraphSamplePtr EventPtr = InEventPtrs[EventIndex];
|
|
TreeView_Base->SetItemExpansion( EventPtr, bShouldExpand );
|
|
TreeView_SetItemsExpansion_Recurrent( EventPtr->GetChildren(), bShouldExpand );
|
|
}
|
|
}
|
|
|
|
void SEventGraph::SetSortModeForColumn( const FName& ColumnID, const EColumnSortMode::Type SortMode )
|
|
{
|
|
ColumnBeingSorted = ColumnID;
|
|
ColumnSortMode = SortMode;
|
|
|
|
SortEvents();
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
ShowSelectedEventsInViewMode
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
void SEventGraph::ShowSelectedEventsInViewMode_Execute( EEventGraphViewModes::Type NewViewMode )
|
|
{
|
|
const TArray<FEventGraphSamplePtr> SelectedEvents = TreeView_Base->GetSelectedItems();
|
|
ShowEventsInViewMode( SelectedEvents, NewViewMode );
|
|
}
|
|
|
|
bool SEventGraph::ShowSelectedEventsInViewMode_CanExecute( EEventGraphViewModes::Type NewViewMode ) const
|
|
{
|
|
return TreeView_Base->GetNumItemsSelected() > 0;
|
|
}
|
|
|
|
ECheckBoxState SEventGraph::ShowSelectedEventsInViewMode_GetCheckState( EEventGraphViewModes::Type NewViewMode ) const
|
|
{
|
|
return GetCurrentStateViewMode() == NewViewMode ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
|
|
}
|
|
|
|
static EEventCompareOps::Type EColumnSortModeToEventCompareOp( const EColumnSortMode::Type ColumnSortMode )
|
|
{
|
|
if( ColumnSortMode == EColumnSortMode::Descending )
|
|
{
|
|
return EEventCompareOps::Greater;
|
|
}
|
|
else if( ColumnSortMode == EColumnSortMode::Ascending )
|
|
{
|
|
return EEventCompareOps::Less;
|
|
}
|
|
check( 0 );
|
|
return EEventCompareOps::InvalidOrMax;
|
|
}
|
|
|
|
void SEventGraph::SortEvents()
|
|
{
|
|
PROFILER_SCOPE_LOG_TIME( TEXT( "SEventGraph::SortEvents" ), nullptr );
|
|
|
|
if( ColumnBeingSorted != NAME_None )
|
|
{
|
|
const FEventGraphColumn& Column = *FEventGraphColumns::Get().ColumnNameToIndexMapping.FindChecked( ColumnBeingSorted );
|
|
|
|
if( GetCurrentStateViewMode() == EEventGraphViewModes::Hierarchical ||
|
|
GetCurrentStateViewMode() == EEventGraphViewModes::FlatInclusive ||
|
|
GetCurrentStateViewMode() == EEventGraphViewModes::FlatInclusiveCoalesced ||
|
|
GetCurrentStateViewMode() == EEventGraphViewModes::FlatExclusive ||
|
|
GetCurrentStateViewMode() == EEventGraphViewModes::FlatExclusiveCoalesced )
|
|
{
|
|
FEventArraySorter::Sort( GetCurrentState()->GetRealRoot()->GetChildren(), Column.ID, EColumnSortModeToEventCompareOp( ColumnSortMode ) );
|
|
FEventArraySorter::Sort( Events_FlatCoalesced, Column.ID, EColumnSortModeToEventCompareOp( ColumnSortMode ) );
|
|
FEventArraySorter::Sort( Events_Flat, Column.ID, EColumnSortModeToEventCompareOp( ColumnSortMode ) );
|
|
|
|
// Update not culled children.
|
|
GetCurrentState()->GetRoot()->SetBooleanStateForAllChildren<EEventPropertyIndex::bNeedNotCulledChildrenUpdate>(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SEventGraph::FilteringSearchBox_OnTextChanged( const FText& InFilterText )
|
|
{
|
|
}
|
|
|
|
static bool RecursiveShowUnfilteredItems(TSharedPtr< STreeView<FEventGraphSamplePtr> >& TreeView, TArray<FEventGraphSamplePtr>& Nodes)
|
|
{
|
|
bool bExpandedAnyChildren = false;
|
|
|
|
for (FEventGraphSamplePtr& Node : Nodes)
|
|
{
|
|
const bool bChildIsExpanded = RecursiveShowUnfilteredItems(TreeView, Node->GetChildren());
|
|
const bool bThisWantsExpanded = !Node->PropertyValueAsBool(EEventPropertyIndex::bIsFiltered);
|
|
const bool bExpandThis = bChildIsExpanded || bThisWantsExpanded;
|
|
bExpandedAnyChildren |= bExpandThis;
|
|
|
|
TreeView->SetItemExpansion(Node, bExpandThis);
|
|
|
|
}
|
|
|
|
return bExpandedAnyChildren;
|
|
}
|
|
|
|
void SEventGraph::FilteringSearchBox_OnTextCommitted(const FText& NewText, ETextCommit::Type CommitType)
|
|
{
|
|
PROFILER_SCOPE_LOG_TIME(TEXT("SEventGraph::FilterOutByText_Execute"), nullptr);
|
|
|
|
SaveCurrentEventGraphState();
|
|
FEventGraphState* Op = GetCurrentState()->CreateCopyWithTextFiltering(NewText.ToString());
|
|
CurrentStateIndex = EventGraphStatesHistory.Insert(MakeShareable(Op), CurrentStateIndex + 1);
|
|
RestoreEventGraphStateFrom(GetCurrentState());
|
|
|
|
// Auto-expand to view the unfiltered items
|
|
if (GetCurrentStateViewMode() == EEventGraphViewModes::Hierarchical)
|
|
{
|
|
RecursiveShowUnfilteredItems(TreeView_Base, GetCurrentState()->GetRoot()->GetChildren());
|
|
TreeView_Refresh();
|
|
}
|
|
}
|
|
|
|
bool SEventGraph::FilteringSearchBox_IsEnabled() const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
|
|
void SEventGraph::OnAggressiveFilteringToggled(ECheckBoxState InState)
|
|
{
|
|
GetCurrentState()->SetAggressiveFiltering( InState == ECheckBoxState::Checked ? true : false );
|
|
|
|
RestoreEventGraphStateFrom(GetCurrentState());
|
|
|
|
// Auto-expand to view the unfiltered items
|
|
if (GetCurrentStateViewMode() == EEventGraphViewModes::Hierarchical)
|
|
{
|
|
RecursiveShowUnfilteredItems(TreeView_Base, GetCurrentState()->GetRoot()->GetChildren());
|
|
TreeView_Refresh();
|
|
}
|
|
}
|
|
|
|
|
|
TSharedPtr<SWidget> SEventGraph::EventGraph_GetMenuContent() const
|
|
{
|
|
const FEventGraphColumn& Column = *FEventGraphColumns::Get().ColumnNameToIndexMapping.FindChecked( HoveredColumnID );
|
|
const TArray<FEventGraphSamplePtr> SelectedEvents = TreeView_Base->GetSelectedItems();
|
|
const int NumSelectedEvents = SelectedEvents.Num();
|
|
FEventGraphSamplePtr SelectedEvent = NumSelectedEvents ? SelectedEvents[0] : nullptr;
|
|
|
|
FText SelectionStr;
|
|
FText PropertyName;
|
|
FText PropertyValue;
|
|
|
|
if( NumSelectedEvents == 0 )
|
|
{
|
|
SelectionStr = LOCTEXT( "NothingSelected", "Nothing selected" );
|
|
}
|
|
else if( NumSelectedEvents == 1 )
|
|
{
|
|
SelectionStr = FText::FromString( SelectedEvent->_StatName.GetPlainNameString() );
|
|
PropertyName = Column.ShortName;
|
|
PropertyValue = FText::FromString( SelectedEvent->GetFormattedValue(Column.Index) );
|
|
}
|
|
else
|
|
{
|
|
SelectionStr = LOCTEXT( "MultipleSelection", "Multiple selection" );
|
|
}
|
|
|
|
const bool bShouldCloseWindowAfterMenuSelection = true;
|
|
FMenuBuilder MenuBuilder( bShouldCloseWindowAfterMenuSelection, NULL );
|
|
|
|
// Selection menu
|
|
MenuBuilder.BeginSection("Selection", LOCTEXT("ContextMenu_Header_Selection", "Selection") );
|
|
{
|
|
struct FLocal
|
|
{
|
|
static bool ReturnFalse()
|
|
{
|
|
return false;
|
|
}
|
|
};
|
|
|
|
FUIAction DummyUIAction;
|
|
DummyUIAction.CanExecuteAction = FCanExecuteAction::CreateStatic( &FLocal::ReturnFalse );
|
|
MenuBuilder.AddMenuEntry
|
|
(
|
|
SelectionStr,
|
|
LOCTEXT("ContextMenu_Selection", "Currently selected events"),
|
|
FSlateIcon(FProfilerStyle::Get().GetStyleSetName(), "@missing.icon"), DummyUIAction, NAME_None, EUserInterfaceActionType::Button
|
|
);
|
|
}
|
|
MenuBuilder.EndSection();
|
|
|
|
// Root menu
|
|
MenuBuilder.BeginSection("Root/Culling/Filtering", LOCTEXT("ContextMenu_Header_Root", "Root") );
|
|
{
|
|
MenuBuilder.AddMenuEntry
|
|
(
|
|
LOCTEXT("ContextMenu_Root_Set", "Set Root"),
|
|
LOCTEXT("ContextMenu_Root_Set_Desc", "Sets the root to the selected event(s) and switches to hierarchical view"),
|
|
FSlateIcon(FProfilerStyle::Get().GetStyleSetName(), "Profiler.EventGraph.SetRoot"), SetRoot_Custom(), NAME_None, EUserInterfaceActionType::Button
|
|
);
|
|
|
|
FUIAction Action_AggregateForSelection;
|
|
|
|
// MenuBuilder.AddMenuEntry
|
|
// (
|
|
// LOCTEXT("ContextMenu_Root_Reset", "Reset To Default"),
|
|
// LOCTEXT("ContextMenu_Root_Reset_Desc", "Resets the root options and removes from the history"),
|
|
// TEXT("Profiler.Misc.ResetToDefault"), ResetRoot_Custom(), NAME_None, EUserInterfaceActionType::Button
|
|
// );
|
|
}
|
|
//MenuBuilder.EndSection();
|
|
|
|
// Culling menu
|
|
//MenuBuilder.BeginSection("Culling", LOCTEXT("ContextMenu_Culling","Culling") );
|
|
{
|
|
FText CullingDesc;
|
|
|
|
if( !Column.bCanBeCulled )
|
|
{
|
|
CullingDesc = LOCTEXT("ContextMenu_Culling_DescErrCol","Culling not available, please select a different column");
|
|
}
|
|
else if( NumSelectedEvents == 1 )
|
|
{
|
|
CullingDesc = FText::Format( LOCTEXT("ContextMenu_Culling_DescFmt","Cull events to '{0}' based on '{1}'"), PropertyValue, PropertyName );
|
|
}
|
|
else
|
|
{
|
|
CullingDesc = LOCTEXT("ContextMenu_Culling_DescErrEve","Culling not available, please select one event");
|
|
}
|
|
|
|
MenuBuilder.AddMenuEntry
|
|
(
|
|
CullingDesc,
|
|
LOCTEXT("ContextMenu_Culling_Desc_TT","Culls the event graph based on the property value of the selected event"),
|
|
FSlateIcon(FProfilerStyle::Get().GetStyleSetName(), "Profiler.EventGraph.CullEvents"), CullByProperty_Custom(SelectedEvent,HoveredColumnID,false), NAME_None, EUserInterfaceActionType::Button
|
|
);
|
|
|
|
// Filtering menu
|
|
FText FilteringDesc;
|
|
|
|
if( !Column.bCanBeFiltered )
|
|
{
|
|
FilteringDesc = LOCTEXT("ContextMenu_Filtering_DescErrCol","Filtering not available, please select a different column");
|
|
}
|
|
else if( NumSelectedEvents == 1 )
|
|
{
|
|
FilteringDesc = FText::Format( LOCTEXT("ContextMenu_Filtering_DescFmt","Filter events to '{0}' based on '{1}'"), PropertyValue, PropertyName );
|
|
}
|
|
else
|
|
{
|
|
FilteringDesc = LOCTEXT("ContextMenu_Filtering_DescErrEve","Filtering not available, please select one event");
|
|
}
|
|
|
|
MenuBuilder.AddMenuEntry
|
|
(
|
|
FilteringDesc,
|
|
LOCTEXT("ContextMenu_Filtering_Desc_TT","Filters the event graph based on the property value of the selected event"),
|
|
FSlateIcon(FProfilerStyle::Get().GetStyleSetName(), "Profiler.EventGraph.FilterEvents"), FilterOutByProperty_Custom(SelectedEvent,HoveredColumnID,false), NAME_None, EUserInterfaceActionType::Button
|
|
);
|
|
|
|
MenuBuilder.AddMenuSeparator();
|
|
MenuBuilder.AddMenuEntry
|
|
(
|
|
LOCTEXT("ContextMenu_ClearHistory", "Reset to default"),
|
|
LOCTEXT("ContextMenu_ClearHistory_Reset_Desc", "For the selected event graph resets root/culling/filter to the default state and clears the history"),
|
|
FSlateIcon(FProfilerStyle::Get().GetStyleSetName(), "Profiler.Misc.ResetToDefault"), ClearHistory_Custom(), NAME_None, EUserInterfaceActionType::Button
|
|
);
|
|
}
|
|
MenuBuilder.EndSection();
|
|
|
|
MenuBuilder.BeginSection("Expand", LOCTEXT("ContextMenu_Header_Expand", "Expand") );
|
|
{
|
|
MenuBuilder.AddMenuEntry
|
|
(
|
|
LOCTEXT("ContextMenu_Header_Expand_ExpandAll", "Expand All"),
|
|
LOCTEXT("ContextMenu_Header_Expand_ExpandAll_Desc", "Expands all events"),
|
|
FSlateIcon(FProfilerStyle::Get().GetStyleSetName(), "Profiler.EventGraph.ExpandAll"), SetExpansionForEvents_Custom( ESelectedEventTypes::AllEvents, true ), NAME_None, EUserInterfaceActionType::Button
|
|
);
|
|
|
|
MenuBuilder.AddMenuEntry
|
|
(
|
|
LOCTEXT("ContextMenu_Header_Expand_CollapseAll", "Collapse All"),
|
|
LOCTEXT("ContextMenu_Header_Expand_CollapseAll_Desc", "Collapses all events"),
|
|
FSlateIcon(FProfilerStyle::Get().GetStyleSetName(), "Profiler.EventGraph.CollapseAll"), SetExpansionForEvents_Custom( ESelectedEventTypes::AllEvents, false ), NAME_None, EUserInterfaceActionType::Button
|
|
);
|
|
|
|
MenuBuilder.AddMenuEntry
|
|
(
|
|
LOCTEXT("ContextMenu_Header_Expand_ExpandSelection", "Expand Selection"),
|
|
LOCTEXT("ContextMenu_Header_Expand_ExpandSelection_Desc", "Expands selected events"),
|
|
FSlateIcon(FProfilerStyle::Get().GetStyleSetName(), "Profiler.EventGraph.ExpandSelection"), SetExpansionForEvents_Custom( ESelectedEventTypes::SelectedEvents, true ), NAME_None, EUserInterfaceActionType::Button
|
|
);
|
|
|
|
MenuBuilder.AddMenuEntry
|
|
(
|
|
LOCTEXT("ContextMenu_Header_Expand_CollapseSelection", "Collapse Selection"),
|
|
LOCTEXT("ContextMenu_Header_Expand_CollapseSelection_Desc", "Collapses selected events"),
|
|
FSlateIcon(FProfilerStyle::Get().GetStyleSetName(), "Profiler.EventGraph.CollapseSelection"), SetExpansionForEvents_Custom( ESelectedEventTypes::SelectedEvents, false ), NAME_None, EUserInterfaceActionType::Button
|
|
);
|
|
|
|
MenuBuilder.AddMenuEntry
|
|
(
|
|
LOCTEXT("ContextMenu_Header_Expand_ExpandThread", "Expand Thread"),
|
|
LOCTEXT("ContextMenu_Header_Expand_ExpandThread_Desc", "Expands selected threads"),
|
|
FSlateIcon(FProfilerStyle::Get().GetStyleSetName(), "Profiler.EventGraph.ExpandThread"), SetExpansionForEvents_Custom( ESelectedEventTypes::SelectedThreadEvents, true ), NAME_None, EUserInterfaceActionType::Button
|
|
);
|
|
|
|
MenuBuilder.AddMenuEntry
|
|
(
|
|
LOCTEXT("ContextMenu_Header_Expand_CollapseThread", "Collapse Thread"),
|
|
LOCTEXT("ContextMenu_Header_Expand_CollapseThread_Desc", "Collapses selected threads"),
|
|
FSlateIcon(FProfilerStyle::Get().GetStyleSetName(), "Profiler.EventGraph.CollapseThread"), SetExpansionForEvents_Custom( ESelectedEventTypes::SelectedThreadEvents, false ), NAME_None, EUserInterfaceActionType::Button
|
|
);
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
FUIAction Action_ExpandHotPath
|
|
(
|
|
FExecuteAction::CreateSP( const_cast<SEventGraph*>(this), &SEventGraph::ContextMenu_ExpandHotPath_Execute ),
|
|
FCanExecuteAction::CreateSP( this, &SEventGraph::ContextMenu_ExpandHotPath_CanExecute )
|
|
);
|
|
MenuBuilder.AddMenuEntry
|
|
(
|
|
LOCTEXT("ContextMenu_Header_Expand_ExpandHotPath", "Expand Hot Path"),
|
|
LOCTEXT("ContextMenu_Header_Expand_ExpandHotPath_Desc", "Expands hot path for the selected events, based on the inclusive time, also enables descending sorting by inclusive time"),
|
|
FSlateIcon(FProfilerStyle::Get().GetStyleSetName(), "Profiler.EventGraph.ExpandHotPath"), Action_ExpandHotPath, NAME_None, EUserInterfaceActionType::Button
|
|
);
|
|
}
|
|
MenuBuilder.EndSection();
|
|
|
|
MenuBuilder.BeginSection("Navigation", LOCTEXT("ContextMenu_Header_Navigation", "Navigation") );
|
|
{
|
|
MenuBuilder.AddMenuEntry
|
|
(
|
|
LOCTEXT("ContextMenu_Header_Navigation_ShowInHierarchicalView", "Show In Hierarchical View"),
|
|
LOCTEXT("ContextMenu_Header_Navigation_ShowInHierarchicalView_Desc", "Switches to hierarchical view and expands selected events"),
|
|
FSlateIcon(FProfilerStyle::Get().GetStyleSetName(), EEventGraphViewModes::ToBrushName(EEventGraphViewModes::Hierarchical)), ShowSelectedEventsInViewMode_Custom(EEventGraphViewModes::Hierarchical), NAME_None, EUserInterfaceActionType::Check
|
|
);
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
MenuBuilder.AddMenuEntry
|
|
(
|
|
LOCTEXT("ContextMenu_Header_Navigation_ShowInFlatView", "Show In FlatInclusive View"),
|
|
LOCTEXT("ContextMenu_Header_Navigation_ShowInFlatView_Desc", "Switches to flat view, also enables descending sorting by inclusive time"),
|
|
FSlateIcon(FProfilerStyle::Get().GetStyleSetName(), EEventGraphViewModes::ToBrushName(EEventGraphViewModes::FlatExclusive)), ShowSelectedEventsInViewMode_Custom(EEventGraphViewModes::FlatExclusive), NAME_None, EUserInterfaceActionType::Check
|
|
);
|
|
|
|
if( FProfilerManager::GetSettings().bShowCoalescedViewModesInEventGraph )
|
|
{
|
|
MenuBuilder.AddMenuEntry
|
|
(
|
|
LOCTEXT("ContextMenu_Header_Navigation_ShowInFlatCoalesced", "Show In FlatInclusive Coalesced"),
|
|
LOCTEXT("ContextMenu_Header_Navigation_ShowInFlatCoalesced_Desc", "Switches to flat coalesced, also enables descending sorting by inclusive time"),
|
|
FSlateIcon(FProfilerStyle::Get().GetStyleSetName(), EEventGraphViewModes::ToBrushName(EEventGraphViewModes::FlatInclusiveCoalesced)), ShowSelectedEventsInViewMode_Custom(EEventGraphViewModes::FlatInclusiveCoalesced), NAME_None, EUserInterfaceActionType::Check
|
|
);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
MenuBuilder.AddMenuEntry
|
|
(
|
|
LOCTEXT("ContextMenu_Header_Navigation_ShowInFlatExclusiveView", "Show In Flat Exclusive View"),
|
|
LOCTEXT("ContextMenu_Header_Navigation_ShowInFlatExclusiveView_Desc", "Switches to flat exclusive view, also enables ascending sorting by exclusive time"),
|
|
FSlateIcon(FProfilerStyle::Get().GetStyleSetName(), EEventGraphViewModes::ToBrushName(EEventGraphViewModes::FlatExclusive)), ShowSelectedEventsInViewMode_Custom(EEventGraphViewModes::FlatExclusive), NAME_None, EUserInterfaceActionType::Check
|
|
);
|
|
|
|
if( FProfilerManager::GetSettings().bShowCoalescedViewModesInEventGraph )
|
|
{
|
|
MenuBuilder.AddMenuEntry
|
|
(
|
|
LOCTEXT("ContextMenu_Header_Navigation_ShowInFlatExclusiveCoalescedView", "Show In Flat Exclusive Coalesced View"),
|
|
LOCTEXT("ContextMenu_Header_Navigation_ShowInFlatExclusiveCoalescedView_Desc", "Switches to flat exclusive coalesced view, also enables ascending sorting by exclusive time enabled"),
|
|
FSlateIcon(FProfilerStyle::Get().GetStyleSetName(), EEventGraphViewModes::ToBrushName(EEventGraphViewModes::FlatExclusiveCoalesced)), ShowSelectedEventsInViewMode_Custom(EEventGraphViewModes::FlatExclusiveCoalesced), NAME_None, EUserInterfaceActionType::Check
|
|
);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
FUIAction Action_ShowInClassAggregate;
|
|
FUIAction Action_ShowInGraphPanel;
|
|
|
|
// Highlight all occurrences based on object's name/class
|
|
FUIAction Action_HighlightBasedOnClass;
|
|
|
|
}
|
|
MenuBuilder.EndSection();
|
|
|
|
/*
|
|
Event graph coloring based on inclusive time as a gradient from black to red?
|
|
*/
|
|
|
|
MenuBuilder.BeginSection("Misc", LOCTEXT("ContextMenu_Header_Misc", "Miscellaneous") );
|
|
{
|
|
FUIAction Action_CopySelectedToClipboard
|
|
(
|
|
FExecuteAction::CreateSP( const_cast<SEventGraph*>(this), &SEventGraph::ContextMenu_CopySelectedToClipboard_Execute ),
|
|
FCanExecuteAction::CreateSP( this, &SEventGraph::ContextMenu_CopySelectedToClipboard_CanExecute )
|
|
);
|
|
MenuBuilder.AddMenuEntry
|
|
(
|
|
LOCTEXT("ContextMenu_Header_Misc_CopySelectedToClipboard", "Copy To Clipboard"),
|
|
LOCTEXT("ContextMenu_Header_Misc_CopySelectedToClipboard_Desc", "Copies selection to clipboard"),
|
|
FSlateIcon(FProfilerStyle::Get().GetStyleSetName(), "Profiler.Misc.CopyToClipboard"), Action_CopySelectedToClipboard, NAME_None, EUserInterfaceActionType::Button
|
|
);
|
|
|
|
FUIAction Action_SaveSelectedToFile;
|
|
|
|
FUIAction Action_SelectStack
|
|
(
|
|
FExecuteAction::CreateSP( const_cast<SEventGraph*>(this), &SEventGraph::ContextMenu_SelectStack_Execute ),
|
|
FCanExecuteAction::CreateSP( this, &SEventGraph::ContextMenu_SelectStack_CanExecute )
|
|
);
|
|
MenuBuilder.AddMenuEntry
|
|
(
|
|
LOCTEXT("ContextMenu_Header_Misc_SelectStack", "Select Stack"),
|
|
LOCTEXT("ContextMenu_Header_Misc_SelectStack_Desc", "Selects all events in the stack"),
|
|
FSlateIcon(FProfilerStyle::Get().GetStyleSetName(), "Profiler.EventGraph.SelectStack"), Action_SelectStack, NAME_None, EUserInterfaceActionType::Button
|
|
);
|
|
|
|
MenuBuilder.AddSubMenu
|
|
(
|
|
LOCTEXT("ContextMenu_Header_Misc_Sort", "Sort By"),
|
|
LOCTEXT("ContextMenu_Header_Misc_Sort_Desc", "Sort by column"),
|
|
FNewMenuDelegate::CreateSP( const_cast<SEventGraph*>(this), &SEventGraph::EventGraph_BuildSortByMenu ),
|
|
false,
|
|
FSlateIcon(FProfilerStyle::Get().GetStyleSetName(), "Profiler.Misc.SortBy")
|
|
);
|
|
}
|
|
MenuBuilder.EndSection();
|
|
|
|
MenuBuilder.BeginSection("Columns", LOCTEXT("ContextMenu_Header_Columns", "Columns") );
|
|
{
|
|
MenuBuilder.AddSubMenu
|
|
(
|
|
LOCTEXT("ContextMenu_Header_Columns_View", "View Column"),
|
|
LOCTEXT("ContextMenu_Header_Columns_View_Desc", "Hides or shows columns"),
|
|
FNewMenuDelegate::CreateSP( const_cast<SEventGraph*>(this), &SEventGraph::EventGraph_BuildViewColumnMenu ),
|
|
false,
|
|
FSlateIcon(FProfilerStyle::Get().GetStyleSetName(), "Profiler.EventGraph.ViewColumn")
|
|
);
|
|
|
|
FUIAction Action_ResetColumns
|
|
(
|
|
FExecuteAction::CreateSP( const_cast<SEventGraph*>(this), &SEventGraph::ContextMenu_ResetColumns_Execute ),
|
|
FCanExecuteAction::CreateSP( this, &SEventGraph::ContextMenu_ResetColumns_CanExecute )
|
|
);
|
|
MenuBuilder.AddMenuEntry
|
|
(
|
|
LOCTEXT("ContextMenu_Header_Columns_ResetColumns", "Reset Columns To Default"),
|
|
LOCTEXT("ContextMenu_Header_Columns_ResetColumns_Desc", "Resets columns to default"),
|
|
FSlateIcon(FProfilerStyle::Get().GetStyleSetName(), "Profiler.EventGraph.ResetColumn"), Action_ResetColumns, NAME_None, EUserInterfaceActionType::Button
|
|
);
|
|
}
|
|
MenuBuilder.EndSection();
|
|
|
|
return MenuBuilder.MakeWidget();
|
|
}
|
|
|
|
void SEventGraph::EventGraph_BuildSortByMenu( FMenuBuilder& MenuBuilder )
|
|
{
|
|
// TODO: Refactor later @see TSharedPtr<SWidget> SCascadePreviewViewportToolBar::GenerateViewMenu() const
|
|
MenuBuilder.BeginSection("ColumnName", LOCTEXT("ContextMenu_Header_Misc_ColumnName","Column Name"));
|
|
for( auto It = TreeViewHeaderColumns.CreateConstIterator(); It; ++It )
|
|
{
|
|
const FEventGraphColumn& Column = It.Value();
|
|
|
|
if( Column.bIsVisible && Column.bCanBeSorted )
|
|
{
|
|
FUIAction Action_SortByColumn
|
|
(
|
|
FExecuteAction::CreateSP( this, &SEventGraph::ContextMenu_SortByColumn_Execute, Column.ID ),
|
|
FCanExecuteAction::CreateSP( this, &SEventGraph::ContextMenu_SortByColumn_CanExecute, Column.ID ),
|
|
FIsActionChecked::CreateSP( this, &SEventGraph::ContextMenu_SortByColumn_IsChecked, Column.ID )
|
|
);
|
|
MenuBuilder.AddMenuEntry
|
|
(
|
|
Column.ShortName,
|
|
Column.Description,
|
|
FSlateIcon(), Action_SortByColumn, NAME_None, EUserInterfaceActionType::RadioButton
|
|
);
|
|
}
|
|
}
|
|
MenuBuilder.EndSection();
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
MenuBuilder.BeginSection("SortMode", LOCTEXT("ContextMenu_Header_Misc_Sort_SortMode", "Sort Mode") );
|
|
{
|
|
FUIAction Action_SortAscending
|
|
(
|
|
FExecuteAction::CreateSP( this, &SEventGraph::ContextMenu_SortMode_Execute, EColumnSortMode::Ascending ),
|
|
FCanExecuteAction::CreateSP( this, &SEventGraph::ContextMenu_SortMode_CanExecute, EColumnSortMode::Ascending ),
|
|
FIsActionChecked::CreateSP( this, &SEventGraph::ContextMenu_SortMode_IsChecked, EColumnSortMode::Ascending )
|
|
);
|
|
MenuBuilder.AddMenuEntry
|
|
(
|
|
LOCTEXT("ContextMenu_Header_Misc_Sort_SortAscending", "Sort Ascending"),
|
|
LOCTEXT("ContextMenu_Header_Misc_Sort_SortAscending_Desc", "Sorts ascending"),
|
|
FSlateIcon(FProfilerStyle::Get().GetStyleSetName(), "Profiler.Misc.SortAscending"), Action_SortAscending, NAME_None, EUserInterfaceActionType::RadioButton
|
|
);
|
|
|
|
FUIAction Action_SortDescending
|
|
(
|
|
FExecuteAction::CreateSP( this, &SEventGraph::ContextMenu_SortMode_Execute, EColumnSortMode::Descending ),
|
|
FCanExecuteAction::CreateSP( this, &SEventGraph::ContextMenu_SortMode_CanExecute, EColumnSortMode::Descending ),
|
|
FIsActionChecked::CreateSP( this, &SEventGraph::ContextMenu_SortMode_IsChecked, EColumnSortMode::Descending )
|
|
);
|
|
MenuBuilder.AddMenuEntry
|
|
(
|
|
LOCTEXT("ContextMenu_Header_Misc_Sort_SortDescending", "Sort Descending"),
|
|
LOCTEXT("ContextMenu_Header_Misc_Sort_SortDescending_Desc", "Sorts descending"),
|
|
FSlateIcon(FProfilerStyle::Get().GetStyleSetName(), "Profiler.Misc.SortDescending"), Action_SortDescending, NAME_None, EUserInterfaceActionType::RadioButton
|
|
);
|
|
}
|
|
MenuBuilder.EndSection();
|
|
}
|
|
|
|
void SEventGraph::EventGraph_BuildViewColumnMenu( FMenuBuilder& MenuBuilder )
|
|
{
|
|
MenuBuilder.BeginSection("ViewColumn", LOCTEXT("ContextMenu_Header_Columns_View", "View Column") );
|
|
|
|
for( auto It = TreeViewHeaderColumns.CreateConstIterator(); It; ++It )
|
|
{
|
|
const FEventGraphColumn& Column = It.Value();
|
|
|
|
FUIAction Action_ToggleColumn
|
|
(
|
|
FExecuteAction::CreateSP( this, &SEventGraph::ContextMenu_ToggleColumn_Execute, Column.ID ),
|
|
FCanExecuteAction::CreateSP( this, &SEventGraph::ContextMenu_ToggleColumn_CanExecute, Column.ID ),
|
|
FIsActionChecked::CreateSP( this, &SEventGraph::ContextMenu_ToggleColumn_IsChecked, Column.ID )
|
|
);
|
|
MenuBuilder.AddMenuEntry
|
|
(
|
|
Column.ShortName,
|
|
Column.Description,
|
|
FSlateIcon(), Action_ToggleColumn, NAME_None, EUserInterfaceActionType::ToggleButton
|
|
);
|
|
}
|
|
|
|
MenuBuilder.EndSection();
|
|
}
|
|
|
|
void SEventGraph::EventGraphViewMode_OnCheckStateChanged( ECheckBoxState NewRadioState, const EEventGraphViewModes::Type InViewMode )
|
|
{
|
|
if( NewRadioState == ECheckBoxState::Checked && GetCurrentStateViewMode() != InViewMode)
|
|
{
|
|
const TArray< FEventGraphSamplePtr > SelectedEvents = TreeView_Base->GetSelectedItems();
|
|
ShowEventsInViewMode( SelectedEvents, InViewMode );
|
|
}
|
|
}
|
|
|
|
ECheckBoxState SEventGraph::EventGraphViewMode_IsChecked( const EEventGraphViewModes::Type InViewMode ) const
|
|
{
|
|
return (GetCurrentStateViewMode() == InViewMode) ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
|
|
}
|
|
|
|
void SEventGraph::EventGraphType_OnCheckStateChanged( ECheckBoxState NewRadioState, const EEventGraphTypes::Type NewEventGraphType )
|
|
{
|
|
const uint32 NumFrames = GetCurrentState()->GetNumFrames();
|
|
|
|
if( NewRadioState == ECheckBoxState::Checked && GetCurrentStateEventGraphType() != NewEventGraphType )
|
|
{
|
|
FEventGraphStateRef EventGraphState = GetCurrentState();
|
|
GetHierarchicalExpandedEvents( EventGraphState->ExpandedEvents );
|
|
GetHierarchicalSelectedEvents( EventGraphState->SelectedEvents );
|
|
|
|
EventGraphState->UpdateToNewEventGraphType( NewEventGraphType );
|
|
SetEventGraphFromStateInternal( EventGraphState );
|
|
}
|
|
}
|
|
|
|
ECheckBoxState SEventGraph::EventGraphType_IsChecked( const EEventGraphTypes::Type InEventGraphType ) const
|
|
{
|
|
if( IsEventGraphStatesHistoryValid() )
|
|
{
|
|
const uint32 NumFrames = GetCurrentState()->GetNumFrames();
|
|
|
|
if( NumFrames == 1 )
|
|
{
|
|
return GetCurrentStateEventGraphType() == InEventGraphType ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
|
|
}
|
|
else if( NumFrames > 1 )
|
|
{
|
|
return GetCurrentStateEventGraphType() == InEventGraphType ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
|
|
}
|
|
}
|
|
|
|
return ECheckBoxState::Unchecked;
|
|
}
|
|
|
|
bool SEventGraph::EventGraphType_IsEnabled( const EEventGraphTypes::Type InEventGraphType ) const
|
|
{
|
|
if( IsEventGraphStatesHistoryValid() )
|
|
{
|
|
const uint32 NumFrames = GetCurrentState()->GetNumFrames();
|
|
if( InEventGraphType == EEventGraphTypes::OneFrame )
|
|
{
|
|
return NumFrames == 1 ? true : false;
|
|
}
|
|
else
|
|
{
|
|
return NumFrames > 1 ? true : false;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void SEventGraph::SetTreeItemsForViewMode( const EEventGraphViewModes::Type NewViewMode, EEventGraphTypes::Type NewEventGraphType )
|
|
{
|
|
GetCurrentState()->ViewMode = NewViewMode;
|
|
GetCurrentState()->EventGraphType = NewEventGraphType;
|
|
|
|
GetCurrentState()->ApplyCulling();
|
|
GetCurrentState()->ApplyFiltering();
|
|
|
|
if( GetCurrentStateViewMode() == EEventGraphViewModes::Hierarchical )
|
|
{
|
|
TreeView_Base->SetTreeItemsSource( &GetCurrentState()->GetRoot()->GetChildren() );
|
|
}
|
|
else if( GetCurrentStateViewMode() == EEventGraphViewModes::FlatInclusive || GetCurrentStateViewMode() == EEventGraphViewModes::FlatExclusive )
|
|
{
|
|
TreeView_Base->SetTreeItemsSource( &Events_Flat );
|
|
}
|
|
else if( GetCurrentStateViewMode() == EEventGraphViewModes::FlatInclusiveCoalesced || GetCurrentStateViewMode() == EEventGraphViewModes::FlatExclusiveCoalesced )
|
|
{
|
|
TreeView_Base->SetTreeItemsSource( &Events_FlatCoalesced );
|
|
}
|
|
}
|
|
|
|
FReply SEventGraph::ExpandHotPath_OnClicked()
|
|
{
|
|
ContextMenu_ExpandHotPath_Execute();
|
|
return FReply::Handled();
|
|
}
|
|
|
|
void SEventGraph::HighlightHotPath_OnCheckStateChanged( ECheckBoxState InState )
|
|
{
|
|
|
|
}
|
|
|
|
void SEventGraph::TreeView_Refresh()
|
|
{
|
|
if( TreeView_Base.IsValid() )
|
|
{
|
|
TreeView_Base->RequestTreeRefresh();
|
|
}
|
|
}
|
|
|
|
void SEventGraph::TreeViewHeaderRow_CreateColumnArgs( const uint32 ColumnIndex )
|
|
{
|
|
const FEventGraphColumn Column = FEventGraphColumns::Get().Collection[ColumnIndex];
|
|
SHeaderRow::FColumn::FArguments ColumnArgs;
|
|
|
|
ColumnArgs
|
|
.ColumnId( Column.ID )
|
|
.DefaultLabel( Column.ShortName )
|
|
.SortMode( EColumnSortMode::None )
|
|
.HAlignHeader( HAlign_Fill )
|
|
.VAlignHeader( VAlign_Fill )
|
|
.HeaderContentPadding( TOptional<FMargin>( 2.0f ) )
|
|
.HAlignCell( HAlign_Fill )
|
|
.VAlignCell( VAlign_Fill )
|
|
.SortMode( this, &SEventGraph::TreeViewHeaderRow_GetSortModeForColumn, Column.ID )
|
|
.OnSort( this, &SEventGraph::TreeViewHeaderRow_OnSortModeChanged )
|
|
.FixedWidth( Column.FixedColumnWidth > 0.0f ? Column.FixedColumnWidth : TOptional<float>() )
|
|
.HeaderContent()
|
|
[
|
|
SNew(SHorizontalBox)
|
|
+SHorizontalBox::Slot()
|
|
.HAlign( Column.HorizontalAlignment )
|
|
.VAlign( VAlign_Center )
|
|
[
|
|
SNew(STextBlock)
|
|
.Text( Column.ShortName )
|
|
.ToolTipText( Column.Description )
|
|
]
|
|
]
|
|
.MenuContent()
|
|
[
|
|
TreeViewHeaderRow_GenerateColumnMenu( Column )
|
|
];
|
|
|
|
TreeViewHeaderColumnArgs.Add( Column.ID, ColumnArgs );
|
|
TreeViewHeaderColumns.Add( Column.ID, Column );
|
|
}
|
|
|
|
void SEventGraph::InitializeAndShowHeaderColumns()
|
|
{
|
|
ColumnSortMode = EColumnSortMode::Descending;
|
|
ColumnBeingSorted = FEventGraphColumns::Get().Collection[(uint32)EEventPropertyIndex::InclusiveTimeMS].ID;
|
|
|
|
for (uint32 ColumnIndex = 0; ColumnIndex < FEventGraphColumns::Get().NumColumns; ColumnIndex++)
|
|
{
|
|
TreeViewHeaderRow_CreateColumnArgs( ColumnIndex );
|
|
}
|
|
|
|
for( auto It = TreeViewHeaderColumns.CreateConstIterator(); It; ++It )
|
|
{
|
|
const FEventGraphColumn& Column = It.Value();
|
|
|
|
if( Column.bIsVisible )
|
|
{
|
|
TreeViewHeaderRow_ShowColumn( Column.ID );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void SEventGraph::TreeViewHeaderRow_OnSortModeChanged( const EColumnSortPriority::Type SortPriority, const FName& ColumnID, const EColumnSortMode::Type SortMode )
|
|
{
|
|
SetSortModeForColumn( ColumnID, SortMode );
|
|
TreeView_Refresh();
|
|
}
|
|
|
|
EColumnSortMode::Type SEventGraph::TreeViewHeaderRow_GetSortModeForColumn( const FName ColumnID ) const
|
|
{
|
|
if( ColumnBeingSorted != ColumnID )
|
|
{
|
|
return EColumnSortMode::None;
|
|
}
|
|
|
|
return ColumnSortMode;
|
|
}
|
|
|
|
void SEventGraph::HeaderMenu_HideColumn_Execute( const FName ColumnID )
|
|
{
|
|
FEventGraphColumn& Column = TreeViewHeaderColumns.FindChecked(ColumnID);
|
|
Column.bIsVisible = false;
|
|
TreeViewHeaderRow->RemoveColumn( ColumnID );
|
|
}
|
|
|
|
void SEventGraph::TreeViewHeaderRow_ShowColumn( const FName ColumnID )
|
|
{
|
|
FEventGraphColumn& Column = TreeViewHeaderColumns.FindChecked( ColumnID );
|
|
Column.bIsVisible = true;
|
|
SHeaderRow::FColumn::FArguments& ColumnArgs = TreeViewHeaderColumnArgs.FindChecked( ColumnID );
|
|
|
|
const int32 NumColumns = TreeViewHeaderRow->GetColumns().Num();
|
|
const int32 ColumnIndex = FMath::Max( 0, FMath::Min( (int32)Column.Index, NumColumns ) );
|
|
TreeViewHeaderRow->InsertColumn( ColumnArgs, ColumnIndex );
|
|
}
|
|
|
|
bool SEventGraph::HeaderMenu_HideColumn_CanExecute( const FName ColumnID ) const
|
|
{
|
|
const FEventGraphColumn& Column = TreeViewHeaderColumns.FindChecked(ColumnID);
|
|
return Column.bCanBeHidden;
|
|
}
|
|
|
|
TSharedRef< SWidget > SEventGraph::TreeViewHeaderRow_GenerateColumnMenu( const FEventGraphColumn& Column )
|
|
{
|
|
bool bIsMenuVisible = false;
|
|
const bool bShouldCloseWindowAfterMenuSelection = true;
|
|
FMenuBuilder MenuBuilder( bShouldCloseWindowAfterMenuSelection, NULL );
|
|
{
|
|
if( Column.bCanBeHidden )
|
|
{
|
|
MenuBuilder.BeginSection("Column", LOCTEXT("TreeViewHeaderRow_Header_Column", "Column") );
|
|
|
|
FUIAction Action_HideColumn
|
|
(
|
|
FExecuteAction::CreateSP( this, &SEventGraph::HeaderMenu_HideColumn_Execute, Column.ID ),
|
|
FCanExecuteAction::CreateSP( this, &SEventGraph::HeaderMenu_HideColumn_CanExecute, Column.ID )
|
|
);
|
|
|
|
MenuBuilder.AddMenuEntry
|
|
(
|
|
LOCTEXT("TreeViewHeaderRow_HideColumn", "Hide"),
|
|
LOCTEXT("TreeViewHeaderRow_HideColumn_Desc", "Hides the selected column"),
|
|
FSlateIcon(), Action_HideColumn, NAME_None, EUserInterfaceActionType::Button
|
|
);
|
|
bIsMenuVisible = true;
|
|
|
|
MenuBuilder.EndSection();
|
|
}
|
|
|
|
if( Column.bCanBeSorted )
|
|
{
|
|
MenuBuilder.BeginSection("SortMode", LOCTEXT("ContextMenu_Header_Misc_Sort_SortMode", "Sort Mode") );
|
|
|
|
FUIAction Action_SortAscending
|
|
(
|
|
FExecuteAction::CreateSP( this, &SEventGraph::HeaderMenu_SortMode_Execute, Column.ID, EColumnSortMode::Ascending ),
|
|
FCanExecuteAction::CreateSP( this, &SEventGraph::HeaderMenu_SortMode_CanExecute, Column.ID, EColumnSortMode::Ascending ),
|
|
FIsActionChecked::CreateSP( this, &SEventGraph::HeaderMenu_SortMode_IsChecked, Column.ID, EColumnSortMode::Ascending )
|
|
);
|
|
MenuBuilder.AddMenuEntry
|
|
(
|
|
LOCTEXT("ContextMenu_Header_Misc_Sort_SortAscending", "Sort Ascending"),
|
|
LOCTEXT("ContextMenu_Header_Misc_Sort_SortAscending_Desc", "Sorts ascending"),
|
|
FSlateIcon(FProfilerStyle::Get().GetStyleSetName(), "Profiler.Misc.SortAscending"), Action_SortAscending, NAME_None, EUserInterfaceActionType::RadioButton
|
|
);
|
|
|
|
FUIAction Action_SortDescending
|
|
(
|
|
FExecuteAction::CreateSP( this, &SEventGraph::HeaderMenu_SortMode_Execute, Column.ID, EColumnSortMode::Descending ),
|
|
FCanExecuteAction::CreateSP( this, &SEventGraph::HeaderMenu_SortMode_CanExecute, Column.ID, EColumnSortMode::Descending ),
|
|
FIsActionChecked::CreateSP( this, &SEventGraph::HeaderMenu_SortMode_IsChecked, Column.ID, EColumnSortMode::Descending )
|
|
);
|
|
MenuBuilder.AddMenuEntry
|
|
(
|
|
LOCTEXT("ContextMenu_Header_Misc_Sort_SortDescending", "Sort Descending"),
|
|
LOCTEXT("ContextMenu_Header_Misc_Sort_SortDescending_Desc", "Sorts descending"),
|
|
FSlateIcon(FProfilerStyle::Get().GetStyleSetName(), "Profiler.Misc.SortDescending"), Action_SortDescending, NAME_None, EUserInterfaceActionType::RadioButton
|
|
);
|
|
bIsMenuVisible = true;
|
|
|
|
MenuBuilder.EndSection();
|
|
}
|
|
|
|
if( Column.bCanBeFiltered )
|
|
{
|
|
MenuBuilder.BeginSection("FilterMode", LOCTEXT("ContextMenu_Header_Misc_Filter_FilterMode", "Filter Mode") );
|
|
bIsMenuVisible = true;
|
|
MenuBuilder.EndSection();
|
|
}
|
|
|
|
if( Column.bCanBeCulled )
|
|
{
|
|
MenuBuilder.BeginSection("CullMode", LOCTEXT("ContextMenu_Header_Misc_Cull_CullMode", "Cull Mode") );
|
|
bIsMenuVisible = true;
|
|
MenuBuilder.EndSection();
|
|
}
|
|
}
|
|
|
|
/*
|
|
HEADER COLUMN
|
|
Show top ten
|
|
Show top bottom
|
|
Filter by list (avg, median, 10%,90%,etc.)
|
|
Text box for filtering for each column instead of one text box used for filtering
|
|
Grouping button for flat view modes (show at most X groups, show all groups for names)
|
|
*/
|
|
|
|
return bIsMenuVisible ? MenuBuilder.MakeWidget() : (TSharedRef<SWidget>)SNullWidget::NullWidget;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void SEventGraph::ContextMenu_ExecuteDummy( const FName ActionName )
|
|
{
|
|
#if DEBUG_PROFILER_PERFORMANCE
|
|
UE_LOG( Profiler, Log, TEXT("SEventGraph::ContextMenu_ExecuteDummy -> %s"), *ActionName.ToString() );
|
|
#endif // DEBUG_PROFILER_PERFORMANCE
|
|
}
|
|
|
|
bool SEventGraph::ContextMenu_CanExecuteDummy( const FName ActionName ) const
|
|
{
|
|
#if DEBUG_PROFILER_PERFORMANCE
|
|
UE_LOG( Profiler, Log, TEXT("SEventGraph::ContextMenu_CanExecuteDummy -> %s"), *ActionName.ToString() );
|
|
#endif // DEBUG_PROFILER_PERFORMANCE
|
|
return false;
|
|
}
|
|
|
|
bool SEventGraph::ContextMenu_IsCheckedDummy( const FName ActionName ) const
|
|
{
|
|
#if DEBUG_PROFILER_PERFORMANCE
|
|
UE_LOG( Profiler, Log, TEXT("SEventGraph::ContextMenu_IsCheckedDummy -> %s"), *ActionName.ToString() );
|
|
#endif // DEBUG_PROFILER_PERFORMANCE
|
|
return false;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
UI ACTIONS
|
|
-----------------------------------------------------------------------------*/
|
|
void SEventGraph::ContextMenu_ExpandHotPath_Execute()
|
|
{
|
|
TArray<FEventGraphSamplePtr> SelectedItems = TreeView_Base->GetSelectedItems();
|
|
FEventGraphSamplePtr EventPtr = SelectedItems[0];
|
|
|
|
ColumnSortMode = EColumnSortMode::Descending;
|
|
ColumnBeingSorted = FEventGraphColumns::Get().Collection[(uint32)EEventPropertyIndex::InclusiveTimeMS].ID;
|
|
SortEvents();
|
|
|
|
// Clear hot path
|
|
TreeView_Base->ClearExpandedItems();
|
|
GetCurrentState()->GetRoot()->SetBooleanStateForAllChildren<EEventPropertyIndex::bIsHotPath>(false);
|
|
|
|
FEventGraphSamplePtr LastHotEvent;
|
|
for( FEventGraphSamplePtr HotEvent = EventPtr; HotEvent.IsValid(); HotEvent = HotEvent->GetChildren().Num() > 0 ? HotEvent->GetChildren()[0] : NULL )
|
|
{
|
|
HotEvent->PropertyValueAsBool(EEventPropertyIndex::bIsHotPath) = true;
|
|
|
|
HotEvent->_bIsHotPath = true;
|
|
LastHotEvent = HotEvent;
|
|
}
|
|
|
|
// Expand all events from the bottom to the top most event.
|
|
TArray<FEventGraphSamplePtr> StackToExpand;
|
|
LastHotEvent->GetStack( StackToExpand );
|
|
|
|
for( int EventIndex = StackToExpand.Num()-1; EventIndex >= 0; EventIndex-- )
|
|
{
|
|
TreeView_Base->SetItemExpansion( StackToExpand[EventIndex], true );
|
|
}
|
|
|
|
TreeView_Refresh();
|
|
}
|
|
|
|
bool SEventGraph::ContextMenu_ExpandHotPath_CanExecute() const
|
|
{
|
|
return GetCurrentStateViewMode() == EEventGraphViewModes::Hierarchical && TreeView_Base->GetNumItemsSelected() == 1;
|
|
}
|
|
|
|
void SEventGraph::ContextMenu_CopySelectedToClipboard_Execute()
|
|
{
|
|
TArray<FEventGraphSamplePtr> SelectedEvents = TreeView_Base->GetSelectedItems();
|
|
|
|
FString Result;
|
|
|
|
// Prepare header.
|
|
for (uint32 ColumnIndex = 0; ColumnIndex < FEventGraphColumns::Get().NumColumns; ColumnIndex++)
|
|
{
|
|
const FEventGraphColumn& Column = FEventGraphColumns::Get().Collection[ColumnIndex];
|
|
Result += FString::Printf( TEXT("\"%s\","), *Column.ShortName.ToString() );
|
|
}
|
|
Result += LINE_TERMINATOR;
|
|
|
|
// Prepare selected samples.
|
|
for( int32 EventIndex = 0; EventIndex < SelectedEvents.Num(); EventIndex++ )
|
|
{
|
|
const FEventGraphSamplePtr EventPtr = SelectedEvents[EventIndex];
|
|
|
|
for (uint32 ColumnIndex = 0; ColumnIndex < FEventGraphColumns::Get().NumColumns; ColumnIndex++)
|
|
{
|
|
const FEventGraphColumn& Column = FEventGraphColumns::Get().Collection[ColumnIndex];
|
|
if( Column.Index != EEventPropertyIndex::None )
|
|
{
|
|
const FString FormattedValue = EventPtr->GetFormattedValue( Column.Index );
|
|
Result += FString::Printf( TEXT("\"%s\","), *FormattedValue );
|
|
}
|
|
}
|
|
Result += LINE_TERMINATOR;
|
|
}
|
|
|
|
if( Result.Len() )
|
|
{
|
|
FPlatformApplicationMisc::ClipboardCopy( *Result );
|
|
}
|
|
}
|
|
|
|
bool SEventGraph::ContextMenu_CopySelectedToClipboard_CanExecute() const
|
|
{
|
|
const int32 NumSelectedItems = TreeView_Base->GetNumItemsSelected();
|
|
return NumSelectedItems > 0;
|
|
}
|
|
|
|
void SEventGraph::ContextMenu_SelectStack_Execute()
|
|
{
|
|
TArray<FEventGraphSamplePtr> SelectedEvents = TreeView_Base->GetSelectedItems();
|
|
|
|
TArray<FEventGraphSamplePtr> ArrayStack;
|
|
SelectedEvents[0]->GetStack( ArrayStack );
|
|
|
|
for( int32 Nx = 0; Nx < ArrayStack.Num(); ++Nx )
|
|
{
|
|
TreeView_Base->SetItemSelection( ArrayStack[Nx], true, ESelectInfo::Direct );
|
|
}
|
|
}
|
|
|
|
bool SEventGraph::ContextMenu_SelectStack_CanExecute() const
|
|
{
|
|
bool bResult = false;
|
|
TArray<FEventGraphSamplePtr> SelectedEvents = TreeView_Base->GetSelectedItems();
|
|
if( SelectedEvents.Num() == 1 )
|
|
{
|
|
FEventGraphSamplePtr StackEvent = SelectedEvents[0];
|
|
bResult = StackEvent->GetParent().IsValid() && !StackEvent->GetParent()->IsRoot();
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
void SEventGraph::ContextMenu_SortByColumn_Execute( const FName ColumnID )
|
|
{
|
|
SetSortModeForColumn( ColumnID, EColumnSortMode::Descending );
|
|
TreeView_Refresh();
|
|
}
|
|
|
|
bool SEventGraph::ContextMenu_SortByColumn_CanExecute( const FName ColumnID ) const
|
|
{
|
|
return ColumnID != ColumnBeingSorted;
|
|
}
|
|
|
|
bool SEventGraph::ContextMenu_SortByColumn_IsChecked( const FName ColumnID )
|
|
{
|
|
return ColumnID == ColumnBeingSorted;
|
|
}
|
|
|
|
void SEventGraph::ContextMenu_SortMode_Execute( const EColumnSortMode::Type InSortMode )
|
|
{
|
|
SetSortModeForColumn( ColumnBeingSorted, InSortMode );
|
|
TreeView_Refresh();
|
|
}
|
|
|
|
bool SEventGraph::ContextMenu_SortMode_CanExecute( const EColumnSortMode::Type InSortMode ) const
|
|
{
|
|
return ColumnSortMode != InSortMode;
|
|
}
|
|
|
|
bool SEventGraph::ContextMenu_SortMode_IsChecked( const EColumnSortMode::Type InSortMode )
|
|
{
|
|
return ColumnSortMode == InSortMode;
|
|
}
|
|
|
|
void SEventGraph::ContextMenu_ResetColumns_Execute()
|
|
{
|
|
ColumnSortMode = EColumnSortMode::Descending;
|
|
ColumnBeingSorted = FEventGraphColumns::Get().Collection[(uint32)EEventPropertyIndex::InclusiveTimeMS].ID;
|
|
|
|
for( uint32 ColumnIndex = 0; ColumnIndex < FEventGraphColumns::Get().NumColumns; ColumnIndex++ )
|
|
{
|
|
const FEventGraphColumn& DefaultColumn = FEventGraphColumns::Get().Collection[ColumnIndex];
|
|
const FEventGraphColumn& CurrentColumn = TreeViewHeaderColumns.FindChecked( DefaultColumn.ID );
|
|
|
|
if( DefaultColumn.bIsVisible && !CurrentColumn.bIsVisible )
|
|
{
|
|
TreeViewHeaderRow_ShowColumn( DefaultColumn.ID );
|
|
}
|
|
else if( !DefaultColumn.bIsVisible && CurrentColumn.bIsVisible )
|
|
{
|
|
HeaderMenu_HideColumn_Execute( DefaultColumn.ID );
|
|
}
|
|
}
|
|
}
|
|
|
|
bool SEventGraph::ContextMenu_ResetColumns_CanExecute() const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void SEventGraph::ContextMenu_ToggleColumn_Execute( const FName ColumnID )
|
|
{
|
|
FEventGraphColumn& Column = TreeViewHeaderColumns.FindChecked(ColumnID);
|
|
if( Column.bIsVisible )
|
|
{
|
|
HeaderMenu_HideColumn_Execute( ColumnID );
|
|
}
|
|
else
|
|
{
|
|
TreeViewHeaderRow_ShowColumn( ColumnID );
|
|
}
|
|
}
|
|
|
|
bool SEventGraph::ContextMenu_ToggleColumn_CanExecute( const FName ColumnID ) const
|
|
{
|
|
const FEventGraphColumn& Column = TreeViewHeaderColumns.FindChecked(ColumnID);
|
|
return Column.bCanBeHidden;
|
|
}
|
|
|
|
bool SEventGraph::ContextMenu_ToggleColumn_IsChecked( const FName ColumnID )
|
|
{
|
|
const FEventGraphColumn& Column = TreeViewHeaderColumns.FindChecked(ColumnID);
|
|
return Column.bIsVisible;
|
|
}
|
|
|
|
void SEventGraph::HeaderMenu_SortMode_Execute( const FName ColumnID, const EColumnSortMode::Type InSortMode )
|
|
{
|
|
SetSortModeForColumn( ColumnID, InSortMode );
|
|
TreeView_Refresh();
|
|
}
|
|
|
|
bool SEventGraph::HeaderMenu_SortMode_CanExecute( const FName ColumnID, const EColumnSortMode::Type InSortMode ) const
|
|
{
|
|
const FEventGraphColumn& Column = TreeViewHeaderColumns.FindChecked(ColumnID);
|
|
const bool bIsValid = Column.bCanBeSorted;
|
|
|
|
bool bCanExecute = ColumnBeingSorted != ColumnID ? true : ColumnSortMode != InSortMode;
|
|
bCanExecute = bCanExecute && bIsValid;
|
|
|
|
return bCanExecute;
|
|
}
|
|
|
|
bool SEventGraph::HeaderMenu_SortMode_IsChecked( const FName ColumnID, const EColumnSortMode::Type InSortMode )
|
|
{
|
|
return ColumnBeingSorted == ColumnID && ColumnSortMode == InSortMode;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
CreateEvents_...
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
void SEventGraph::CreateEvents()
|
|
{
|
|
// #YRX_Profiler 2015-12-17 Fix recursive calls accounting.
|
|
|
|
// Linear
|
|
GetCurrentState()->GetRoot()->GetLinearEvents( Events_Flat, true );
|
|
|
|
// Linear Coalesced by Name
|
|
// Event name -> Events
|
|
TMap< FName, TArray<FEventGraphSamplePtr> > FlatIncCoalescedEvents;
|
|
const int32 NumLinearSamples = Events_Flat.Num();
|
|
Events_FlatCoalesced.Reset( NumLinearSamples );
|
|
HierarchicalToFlatCoalesced.Reset();
|
|
|
|
for( int32 LinearEventIndex = 0; LinearEventIndex < NumLinearSamples; LinearEventIndex++ )
|
|
{
|
|
const FEventGraphSamplePtr EventPtr = Events_Flat[LinearEventIndex];
|
|
FlatIncCoalescedEvents.FindOrAdd( EventPtr->_StatName ).Add( EventPtr->DuplicateSimplePtr() );
|
|
HierarchicalToFlatCoalesced.Add( EventPtr->_StatName, EventPtr );
|
|
}
|
|
|
|
// Should ignore recursion!
|
|
for( auto It = FlatIncCoalescedEvents.CreateConstIterator(); It; ++It )
|
|
{
|
|
const TArray<FEventGraphSamplePtr>& InclusiveCoalescedEvents = It.Value();
|
|
|
|
FEventGraphSamplePtr CoalescedEvent = InclusiveCoalescedEvents[0];
|
|
for( int32 EventIndex = 1; EventIndex < InclusiveCoalescedEvents.Num(); EventIndex++ )
|
|
{
|
|
CoalescedEvent->Combine( InclusiveCoalescedEvents[EventIndex] );
|
|
}
|
|
|
|
Events_FlatCoalesced.Add( CoalescedEvent );
|
|
}
|
|
|
|
// CoalesceEventsByColumnName/ID
|
|
}
|
|
|
|
void SEventGraph::ShowEventsInViewMode( const TArray<FEventGraphSamplePtr>& EventsToSynchronize, const EEventGraphViewModes::Type NewViewMode )
|
|
{
|
|
FEventGraphStateRef EventGraphState = GetCurrentState();
|
|
|
|
GetHierarchicalSelectedEvents( EventGraphState->SelectedEvents, &EventsToSynchronize );
|
|
GetHierarchicalExpandedEvents( EventGraphState->ExpandedEvents );
|
|
|
|
SetTreeItemsForViewMode( NewViewMode, GetCurrentStateEventGraphType() );
|
|
|
|
SetHierarchicalSelectedEvents( EventGraphState->SelectedEvents );
|
|
SetHierarchicalExpandedEvents( EventGraphState->ExpandedEvents );
|
|
|
|
EEventPropertyIndex ColumnIndex = FEventGraphColumns::Get().ColumnNameToIndexMapping.FindChecked( ColumnBeingSorted )->Index;
|
|
|
|
if( NewViewMode == EEventGraphViewModes::FlatInclusive || NewViewMode == EEventGraphViewModes::FlatInclusiveCoalesced || NewViewMode == EEventGraphViewModes::Hierarchical )
|
|
{
|
|
ColumnIndex = EEventPropertyIndex::InclusiveTimeMS;
|
|
SetSortModeForColumn( FEventGraphColumns::Get().Collection[(uint32)ColumnIndex].ID, EColumnSortMode::Descending );
|
|
}
|
|
else if( NewViewMode == EEventGraphViewModes::FlatExclusive || NewViewMode == EEventGraphViewModes::FlatExclusiveCoalesced )
|
|
{
|
|
ColumnIndex = EEventPropertyIndex::ExclusiveTimeMS;
|
|
SetSortModeForColumn( FEventGraphColumns::Get().Collection[(uint32)ColumnIndex].ID, EColumnSortMode::Descending );
|
|
}
|
|
|
|
ScrollToTheSlowestSelectedEvent( ColumnIndex );
|
|
|
|
if( NewViewMode == EEventGraphViewModes::Hierarchical )
|
|
{
|
|
TArray<FEventGraphSamplePtr> SelectedEvents = TreeView_Base->GetSelectedItems();
|
|
for( int32 Nx = 0; Nx < SelectedEvents.Num(); ++Nx )
|
|
{
|
|
// Find stack for the specified event and expand that stack.
|
|
FEventGraphSamplePtr EventToExpand = SelectedEvents[Nx];
|
|
TArray<FEventGraphSamplePtr> StackToExpand;
|
|
EventToExpand->GetStack( StackToExpand );
|
|
|
|
for( int32 Index = 0; Index < StackToExpand.Num(); ++Index )
|
|
{
|
|
TreeView_Base->SetItemExpansion( StackToExpand[Index], true );
|
|
}
|
|
}
|
|
}
|
|
|
|
TreeView_Refresh();
|
|
}
|
|
|
|
void SEventGraph::ScrollToTheSlowestSelectedEvent( EEventPropertyIndex ColumnIndex )
|
|
{
|
|
TArray<FEventGraphSamplePtr> SelectedEvents = TreeView_Base->GetSelectedItems();
|
|
if( SelectedEvents.Num() > 0 )
|
|
{
|
|
// Sort events by the inclusive or the exclusive time, depends on the view mode.
|
|
const FEventGraphColumn& Column = FEventGraphColumns::Get().Collection[(uint32)ColumnIndex];
|
|
FEventArraySorter::Sort( SelectedEvents, Column.ID, EEventCompareOps::Greater );
|
|
|
|
// Scroll to the the slowest item.
|
|
TreeView_Base->RequestScrollIntoView( SelectedEvents[0] );
|
|
}
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
Get/Set HierarchicalSelectedEvents
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
void SEventGraph::GetHierarchicalSelectedEvents( TArray< FEventGraphSamplePtr >& out_HierarchicalSelectedEvents, const TArray<FEventGraphSamplePtr>* SelectedEvents /*= NULL*/ ) const
|
|
{
|
|
out_HierarchicalSelectedEvents.Reset();
|
|
|
|
TArray<FEventGraphSamplePtr> ViewSelectedEvents;
|
|
if( SelectedEvents != NULL )
|
|
{
|
|
ViewSelectedEvents = *SelectedEvents;
|
|
}
|
|
else
|
|
{
|
|
ViewSelectedEvents = TreeView_Base->GetSelectedItems();
|
|
}
|
|
|
|
if( GetCurrentStateViewMode() == EEventGraphViewModes::FlatInclusiveCoalesced || GetCurrentStateViewMode() == EEventGraphViewModes::FlatExclusiveCoalesced )
|
|
{
|
|
for( int32 Nx = 0; Nx < ViewSelectedEvents.Num(); ++Nx )
|
|
{
|
|
HierarchicalToFlatCoalesced.MultiFind( ViewSelectedEvents[Nx]->_StatName, out_HierarchicalSelectedEvents );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
out_HierarchicalSelectedEvents = ViewSelectedEvents;
|
|
}
|
|
}
|
|
|
|
void SEventGraph::SetHierarchicalSelectedEvents( const TArray<FEventGraphSamplePtr>& HierarchicalSelectedEvents )
|
|
{
|
|
struct FCoalescedEventMatcher
|
|
{
|
|
FCoalescedEventMatcher( const FEventGraphSamplePtr& InEventPtr )
|
|
: EventPtr( InEventPtr )
|
|
{}
|
|
|
|
bool operator()(const FEventGraphSamplePtr& Other) const
|
|
{
|
|
return EventPtr->_StatName == Other->_StatName;
|
|
}
|
|
protected:
|
|
const FEventGraphSamplePtr& EventPtr;
|
|
};
|
|
|
|
TArray<FEventGraphSamplePtr> SelectedEvents;
|
|
|
|
if( GetCurrentStateViewMode() == EEventGraphViewModes::FlatInclusiveCoalesced || GetCurrentStateViewMode() == EEventGraphViewModes::FlatExclusiveCoalesced )
|
|
{
|
|
for( int32 Nx = 0; Nx < HierarchicalSelectedEvents.Num(); ++Nx )
|
|
{
|
|
const int32 Index = Events_FlatCoalesced.IndexOfByPredicate(FCoalescedEventMatcher(HierarchicalSelectedEvents[Nx]));
|
|
if( Index != INDEX_NONE )
|
|
{
|
|
SelectedEvents.AddUnique( Events_FlatCoalesced[Index] );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SelectedEvents = HierarchicalSelectedEvents;
|
|
}
|
|
|
|
TreeView_Base->ClearSelection();
|
|
for( int32 Nx = 0; Nx < SelectedEvents.Num(); ++Nx )
|
|
{
|
|
TreeView_Base->SetItemSelection( SelectedEvents[Nx], true );
|
|
}
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
Get/Set HierarchicalExpandedEvents
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
void SEventGraph::GetHierarchicalExpandedEvents( TSet<FEventGraphSamplePtr>& out_HierarchicalExpandedEvents ) const
|
|
{
|
|
if( GetCurrentStateViewMode() == EEventGraphViewModes::Hierarchical )
|
|
{
|
|
out_HierarchicalExpandedEvents.Empty();
|
|
TreeView_Base->GetExpandedItems( out_HierarchicalExpandedEvents );
|
|
}
|
|
}
|
|
|
|
void SEventGraph::SetHierarchicalExpandedEvents( const TSet<FEventGraphSamplePtr>& HierarchicalExpandedEvents )
|
|
{
|
|
if( GetCurrentStateViewMode() == EEventGraphViewModes::Hierarchical )
|
|
{
|
|
TreeView_Base->ClearExpandedItems();
|
|
for( auto It = HierarchicalExpandedEvents.CreateConstIterator(); It; ++It )
|
|
{
|
|
TreeView_Base->SetItemExpansion( *It, true );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
Function details
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
FReply SEventGraph::CallingCalledFunctionButton_OnClicked( FEventGraphSamplePtr EventPtr )
|
|
{
|
|
if( !EventPtr->_bIsCulled )
|
|
{
|
|
UpdateFunctionDetailsForEvent( EventPtr );
|
|
|
|
TArray<FEventGraphSamplePtr> Events;
|
|
Events.Add( EventPtr );
|
|
ShowEventsInViewMode( Events, GetCurrentStateViewMode() );
|
|
}
|
|
|
|
return FReply::Handled();
|
|
}
|
|
|
|
void SEventGraph::DisableFunctionDetails()
|
|
{
|
|
(*CurrentFunctionDescSlot )
|
|
[
|
|
SNew(STextBlock)
|
|
.WrapTextAt( 128.0f )
|
|
.Text( LOCTEXT("FunctionDetails_SelectOneEvent","Function details view works only if you select one event. Please select an individual event to proceed.") )
|
|
.TextStyle(FProfilerStyle::Get(), TEXT("Profiler.Tooltip") )
|
|
];
|
|
|
|
VerticalBox_TopCalling->ClearChildren();
|
|
VerticalBox_TopCalled->ClearChildren();
|
|
|
|
HighlightedEventName = NAME_None;
|
|
}
|
|
|
|
void SEventGraph::UpdateFunctionDetailsForEvent( FEventGraphSamplePtr SelectedEvent )
|
|
{
|
|
GenerateCallerCalleeGraph( SelectedEvent );
|
|
|
|
(*CurrentFunctionDescSlot)
|
|
[
|
|
GetContentForEvent(SelectedEvent,1.0f,true)
|
|
];
|
|
|
|
RecreateWidgetsForTopEvents( VerticalBox_TopCalling, TopCallingFunctionEvents );
|
|
RecreateWidgetsForTopEvents( VerticalBox_TopCalled, TopCalledFunctionEvents );
|
|
|
|
HighlightedEventName = SelectedEvent->_StatName;
|
|
}
|
|
|
|
void SEventGraph::UpdateFunctionDetails()
|
|
{
|
|
TArray<FEventGraphSamplePtr> SelectedItems = TreeView_Base->GetSelectedItems();
|
|
if( SelectedItems.Num() == 1 )
|
|
{
|
|
UpdateFunctionDetailsForEvent( SelectedItems[0] );
|
|
}
|
|
else
|
|
{
|
|
DisableFunctionDetails();
|
|
}
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
Function details
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
void SEventGraph::GenerateCallerCalleeGraph( FEventGraphSamplePtr SelectedEvent )
|
|
{
|
|
TArray< FEventGraphSamplePtr > EventsByName;
|
|
HierarchicalToFlatCoalesced.MultiFind( SelectedEvent->_StatName, EventsByName );
|
|
|
|
// Parent
|
|
TSet< FEventGraphSamplePtr > CallingFunctionEventSet;
|
|
for( int32 Nx = 0; Nx < EventsByName.Num(); ++Nx )
|
|
{
|
|
FEventGraphSamplePtr ParentPtr = EventsByName[Nx]->GetParent();
|
|
if( ParentPtr.IsValid() && !ParentPtr->IsRoot() )
|
|
{
|
|
CallingFunctionEventSet.Add( ParentPtr );
|
|
}
|
|
}
|
|
|
|
GenerateTopEvents( CallingFunctionEventSet, TopCallingFunctionEvents );
|
|
CalculateEventWeights( TopCallingFunctionEvents );
|
|
|
|
// Children
|
|
TSet< FEventGraphSamplePtr > CalledFunctionEventSet;
|
|
for( int32 Nx = 0; Nx < EventsByName.Num(); ++Nx )
|
|
{
|
|
CalledFunctionEventSet.Append( EventsByName[Nx]->GetChildren() );
|
|
}
|
|
|
|
GenerateTopEvents( CalledFunctionEventSet, TopCalledFunctionEvents );
|
|
CalculateEventWeights( TopCalledFunctionEvents );
|
|
}
|
|
|
|
void SEventGraph::GenerateTopEvents( const TSet< FEventGraphSamplePtr >& EventPtrSet, TArray<FEventPtrAndMisc>& out_Results )
|
|
{
|
|
const int NumTopEvents = 5;
|
|
TArray<FEventGraphSamplePtr> EventPtrArray = EventPtrSet.Array();
|
|
|
|
// Calculate total time.
|
|
double TotalTimeMS = 0.0f;
|
|
for( int32 Nx = 0; Nx < EventPtrArray.Num(); ++Nx )
|
|
{
|
|
TotalTimeMS += EventPtrArray[Nx]->_InclusiveTimeMS;
|
|
}
|
|
|
|
// Sort events by the inclusive time.
|
|
const FEventGraphColumn& Column = FEventGraphColumns::Get().Collection[(uint32)EEventPropertyIndex::InclusiveTimeMS];
|
|
FEventArraySorter::Sort( EventPtrArray, Column.ID, EEventCompareOps::Greater );
|
|
|
|
// Calculate total time for the top events.
|
|
double Top5TimeMS = 0.0f;
|
|
for( int32 Nx = 0; Nx < EventPtrArray.Num() && Nx < NumTopEvents; ++Nx )
|
|
{
|
|
Top5TimeMS += EventPtrArray[Nx]->_InclusiveTimeMS;
|
|
}
|
|
|
|
// Calculate values for top events.
|
|
out_Results.Reset();
|
|
for( int32 Nx = 0; Nx < EventPtrArray.Num() && Nx < NumTopEvents; ++Nx )
|
|
{
|
|
const FEventGraphSamplePtr EventPtr = EventPtrArray[Nx];
|
|
const float IncTimeToTotalPct = static_cast<float>(EventPtr->_InclusiveTimeMS / TotalTimeMS);
|
|
const float HeightPct = static_cast<float>(EventPtr->_InclusiveTimeMS / Top5TimeMS);
|
|
out_Results.Add( FEventPtrAndMisc(EventPtr,IncTimeToTotalPct,HeightPct) );
|
|
}
|
|
}
|
|
|
|
void SEventGraph::CalculateEventWeights( TArray<FEventPtrAndMisc>& Events )
|
|
{
|
|
// TODO: This value was calculated by hand
|
|
// and gives reasonable results for scaling button in the function details.
|
|
// Maximum number of visible buttons is 5, 5 buttons require 100px, where 20px for each button
|
|
// The height of the area is 190px so 190px/20px = ~9
|
|
const float MaxButtons = 9.0f;
|
|
const float TotalHeightPct = MaxButtons / 5.0f;
|
|
const float MinHeightPct = TotalHeightPct / MaxButtons;
|
|
|
|
// Update min height pct for buttons where the ratio is too low
|
|
float CurrentHeightPct = 0.0f;
|
|
for( int32 Tx = 0; Tx < Events.Num(); ++Tx )
|
|
{
|
|
FEventPtrAndMisc& EventPtr = Events[Tx];
|
|
EventPtr.HeightPct = FMath::Max( EventPtr.HeightPct, MinHeightPct );
|
|
CurrentHeightPct += EventPtr.HeightPct;
|
|
}
|
|
|
|
// Update height pct to fit all button into visible area.
|
|
const float FitHeightPct = TotalHeightPct / CurrentHeightPct;
|
|
for( int32 Tx = 0; Tx < Events.Num(); ++Tx )
|
|
{
|
|
FEventPtrAndMisc& EventPtr = Events[Tx];
|
|
EventPtr.HeightPct *= FitHeightPct;
|
|
}
|
|
}
|
|
|
|
void SEventGraph::RecreateWidgetsForTopEvents( const TSharedPtr<SVerticalBox>& DestVerticalBox, const TArray<FEventPtrAndMisc>& TopEvents )
|
|
{
|
|
DestVerticalBox->ClearChildren();
|
|
for( int32 Nx = 0; Nx < TopEvents.Num(); ++Nx )
|
|
{
|
|
const FEventPtrAndMisc& EventPtrAndPct = TopEvents[Nx];
|
|
|
|
DestVerticalBox->AddSlot()
|
|
.FillHeight( EventPtrAndPct.HeightPct )
|
|
.Padding( 1.0f )
|
|
[
|
|
SNew(SButton)
|
|
.HAlign( HAlign_Left )
|
|
.VAlign( VAlign_Center )
|
|
.TextStyle(FProfilerStyle::Get(), "Profiler.Tooltip" )
|
|
.ContentPadding( FMargin(4.0f, 1.0f) )
|
|
.OnClicked( this, &SEventGraph::CallingCalledFunctionButton_OnClicked, EventPtrAndPct.EventPtr )
|
|
[
|
|
GetContentForEvent( EventPtrAndPct.EventPtr, EventPtrAndPct.IncTimeToTotalPct, false )
|
|
]
|
|
];
|
|
}
|
|
}
|
|
|
|
FString SEventGraph::GetEventDescription( FEventGraphSamplePtr EventPtr, float Pct, const bool bSimple )
|
|
{
|
|
const bool bIgnoreEventName = EventPtr->_ThreadName == EventPtr->_StatName;
|
|
|
|
const FString ThreadName = EventPtr->_ThreadName.GetPlainNameString().LeftChop( 9 );// TODO: Fix this
|
|
const FString EventName = FProfilerHelper::ShortenName(EventPtr->_StatName.GetPlainNameString(),28);
|
|
|
|
const FString ThreadAndEventName = bIgnoreEventName ? ThreadName : FString::Printf( TEXT("%s:%s"), *ThreadName, *EventName );
|
|
|
|
const FString Caption = FString::Printf
|
|
(
|
|
TEXT("%s, %.1f%% (%s)"),
|
|
*ThreadAndEventName,
|
|
Pct*100.0f,
|
|
*EventPtr->GetFormattedValue(EEventPropertyIndex::InclusiveTimeMS)
|
|
);
|
|
|
|
return bSimple ? ThreadAndEventName : Caption;
|
|
}
|
|
|
|
|
|
TSharedRef<SHorizontalBox> SEventGraph::GetContentForEvent( FEventGraphSamplePtr EventPtr, float Pct, const bool bSimple )
|
|
{
|
|
TSharedRef<SHorizontalBox> Content = SNew(SHorizontalBox);
|
|
|
|
Content->AddSlot()
|
|
.AutoWidth()
|
|
.HAlign(HAlign_Center)
|
|
.VAlign(VAlign_Center)
|
|
[
|
|
SNew(STextBlock)
|
|
.Text( FText::FromString(GetEventDescription(EventPtr,Pct,bSimple)) )
|
|
.TextStyle(FProfilerStyle::Get(), bSimple ? TEXT("Profiler.Tooltip") : TEXT("Profiler.EventGraph.DarkText") )
|
|
];
|
|
|
|
if( EventPtr->_bIsCulled )
|
|
{
|
|
Content->AddSlot()
|
|
.AutoWidth()
|
|
.HAlign(HAlign_Center)
|
|
.VAlign(VAlign_Center)
|
|
[
|
|
SNew( SImage )
|
|
.Image(FProfilerStyle::Get().GetBrush("Profiler.EventGraph.CulledEvent") )
|
|
.ToolTipText( LOCTEXT("Misc_EventCulled","Event is culled") )
|
|
];
|
|
}
|
|
|
|
if( EventPtr->_bIsFiltered )
|
|
{
|
|
Content->AddSlot()
|
|
.AutoWidth()
|
|
.HAlign(HAlign_Center)
|
|
.VAlign(VAlign_Center)
|
|
[
|
|
SNew( SImage )
|
|
.Image(FProfilerStyle::Get().GetBrush("Profiler.EventGraph.FilteredEvent") )
|
|
.ToolTipText( LOCTEXT("Misc_EventFiltered","Event is filtered") )
|
|
];
|
|
}
|
|
|
|
Content->AddSlot()
|
|
.AutoWidth()
|
|
.HAlign(HAlign_Center)
|
|
.VAlign(VAlign_Center)
|
|
[
|
|
SNew( SImage )
|
|
.Image(FProfilerStyle::Get().GetBrush("Profiler.Tooltip.HintIcon10") )
|
|
.ToolTip( SEventGraphTooltip::GetTableCellTooltip( EventPtr ) )
|
|
];
|
|
|
|
return Content;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
History management
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
void SEventGraph::SetNewEventGraphState( const FEventGraphDataRef AverageEventGraph, const FEventGraphDataRef MaximumEventGraph, bool bInitial )
|
|
{
|
|
PROFILER_SCOPE_LOG_TIME( TEXT( "SEventGraph::UpdateEventGraph" ), nullptr );
|
|
|
|
// Store current operation.
|
|
SaveCurrentEventGraphState();
|
|
FEventGraphState* Op = new FEventGraphState( AverageEventGraph->DuplicateAsRef(), MaximumEventGraph->DuplicateAsRef() );
|
|
CurrentStateIndex = EventGraphStatesHistory.Add( MakeShareable(Op) );
|
|
RestoreEventGraphStateFrom( GetCurrentState(), bInitial );
|
|
FillThreadFilterOptions();
|
|
}
|
|
|
|
FReply SEventGraph::HistoryBack_OnClicked()
|
|
{
|
|
SwitchToEventGraphState( CurrentStateIndex-1 );
|
|
return FReply::Handled();
|
|
}
|
|
|
|
bool SEventGraph::HistoryBack_IsEnabled() const
|
|
{
|
|
return EventGraphStatesHistory.Num() > 1 && CurrentStateIndex > 0;
|
|
}
|
|
|
|
FText SEventGraph::HistoryBack_GetToolTipText() const
|
|
{
|
|
// TODO: Add a nicer custom tooltip.
|
|
if( HistoryBack_IsEnabled() )
|
|
{
|
|
return FText::Format( LOCTEXT("HistoryBack_Tooltip", "Back to {0}"), EventGraphStatesHistory[CurrentStateIndex-1]->GetFullDescription() );
|
|
}
|
|
|
|
return FText::GetEmpty();
|
|
}
|
|
|
|
FReply SEventGraph::HistoryForward_OnClicked()
|
|
{
|
|
SwitchToEventGraphState( CurrentStateIndex+1 );
|
|
return FReply::Handled();
|
|
}
|
|
|
|
bool SEventGraph::HistoryForward_IsEnabled() const
|
|
{
|
|
return EventGraphStatesHistory.Num() > 1 && CurrentStateIndex < EventGraphStatesHistory.Num()-1;
|
|
}
|
|
|
|
FText SEventGraph::HistoryForward_GetToolTipText() const
|
|
{
|
|
// TODO: Add a nicer custom tooltip.
|
|
if( HistoryForward_IsEnabled() )
|
|
{
|
|
return FText::Format( LOCTEXT("HistoryForward_Tooltip", "Forward to {0}"), EventGraphStatesHistory[CurrentStateIndex+1]->GetFullDescription() );
|
|
}
|
|
|
|
return FText::GetEmpty();
|
|
}
|
|
|
|
bool SEventGraph::EventGraph_IsEnabled() const
|
|
{
|
|
return EventGraphStatesHistory.Num() > 0;
|
|
}
|
|
|
|
bool SEventGraph::HistoryList_IsEnabled() const
|
|
{
|
|
return EventGraphStatesHistory.Num() > 1;
|
|
}
|
|
|
|
TSharedRef<SWidget> SEventGraph::HistoryList_GetMenuContent() const
|
|
{
|
|
FMenuBuilder MenuBuilder(true, NULL);
|
|
|
|
for( int32 StateIndex = 0; StateIndex < EventGraphStatesHistory.Num(); ++StateIndex )
|
|
{
|
|
const FEventGraphStateRef StateRef = EventGraphStatesHistory[StateIndex];
|
|
MenuBuilder.AddMenuEntry
|
|
(
|
|
StateRef->GetFullDescription(),
|
|
FText(),
|
|
FSlateIcon(),
|
|
HistoryList_GoTo_Custom( StateIndex ),
|
|
NAME_None,
|
|
EUserInterfaceActionType::RadioButton
|
|
);
|
|
}
|
|
|
|
return MenuBuilder.MakeWidget();
|
|
}
|
|
|
|
void SEventGraph::HistoryList_GoTo_Execute( int32 StateIndex )
|
|
{
|
|
if( StateIndex != CurrentStateIndex )
|
|
{
|
|
SwitchToEventGraphState( StateIndex );
|
|
}
|
|
}
|
|
|
|
void SEventGraph::SaveCurrentEventGraphState()
|
|
{
|
|
if( EventGraphStatesHistory.Num() > 0 )
|
|
{
|
|
FEventGraphStateRef EventGraphState = GetCurrentState();
|
|
GetHierarchicalExpandedEvents( EventGraphState->ExpandedEvents );
|
|
GetHierarchicalSelectedEvents( EventGraphState->SelectedEvents );
|
|
}
|
|
}
|
|
|
|
void SEventGraph::SetEventGraphFromStateInternal( const FEventGraphStateRef& EventGraphState )
|
|
{
|
|
EventGraphState->ApplyCulling();
|
|
EventGraphState->ApplyFiltering();
|
|
|
|
if( EventGraphState->ExpandedCulledEvents.Num() > 0 )
|
|
{
|
|
const int32 NumChildren = EventGraphState->ExpandedCulledEvents.Num();
|
|
for( int32 Nx = 0; Nx < NumChildren; ++Nx )
|
|
{
|
|
const FEventGraphSamplePtr& EventPtr = EventGraphState->ExpandedCulledEvents[Nx];
|
|
//EventPtr->PropertyValueAsBool( EEventPropertyIndex::bIsCulled) = false;
|
|
EventPtr->_bIsCulled = false;
|
|
EventPtr->RequestNotCulledChildrenUpdate();
|
|
}
|
|
}
|
|
|
|
CreateEvents();
|
|
SetTreeItemsForViewMode( EventGraphState->ViewMode, EventGraphState->EventGraphType );
|
|
|
|
SetHierarchicalSelectedEvents( EventGraphState->SelectedEvents );
|
|
SetHierarchicalExpandedEvents( EventGraphState->ExpandedEvents );
|
|
SortEvents();
|
|
ScrollToTheSlowestSelectedEvent( EEventPropertyIndex::InclusiveTimeMS );
|
|
|
|
UpdateFunctionDetails();
|
|
TreeView_Refresh();
|
|
}
|
|
|
|
|
|
void SEventGraph::RestoreEventGraphStateFrom( const FEventGraphStateRef EventGraphState, const bool bRestoredFromHistoryEvent /*= true*/ )
|
|
{
|
|
SetEventGraphFromStateInternal( EventGraphState );
|
|
|
|
if( bRestoredFromHistoryEvent )
|
|
{
|
|
// Broadcast that a new graph event has been set.
|
|
EventGraphRestoredFromHistoryEvent.Broadcast( EventGraphState->GetEventGraph()->GetFrameStartIndex(), EventGraphState->GetEventGraph()->GetFrameEndIndex() );
|
|
}
|
|
}
|
|
|
|
void SEventGraph::SwitchToEventGraphState( int32 StateIndex )
|
|
{
|
|
SaveCurrentEventGraphState();
|
|
CurrentStateIndex = StateIndex;
|
|
RestoreEventGraphStateFrom( GetCurrentState() );
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
UI Actions
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
void SEventGraph::BindCommands()
|
|
{
|
|
Map_SelectAllFrames_Global();
|
|
}
|
|
|
|
void SEventGraph::SetRoot_Execute()
|
|
{
|
|
TArray<FEventGraphSamplePtr> SelectedLeafs;
|
|
GetHierarchicalSelectedEvents( SelectedLeafs );
|
|
TMap< FEventGraphSamplePtr, TSet<FEventGraphSamplePtr> > StacksForSelectedLeafs;
|
|
|
|
// Grab stack for all selected events.
|
|
for( int32 Nx = 0; Nx < SelectedLeafs.Num(); ++Nx )
|
|
{
|
|
const FEventGraphSamplePtr SelectedLeaf = SelectedLeafs[Nx];
|
|
TArray<FEventGraphSamplePtr> ArrayStack;
|
|
SelectedLeaf->GetStack( ArrayStack );
|
|
|
|
TSet<FEventGraphSamplePtr> SetStack;
|
|
SetStack.Append( ArrayStack );
|
|
|
|
StacksForSelectedLeafs.Add( SelectedLeaf, SetStack );
|
|
}
|
|
|
|
// Remove duplicated stacks.
|
|
// Not super efficient, but should be ok for now.
|
|
for( auto OuterIt = StacksForSelectedLeafs.CreateIterator(); OuterIt; ++OuterIt )
|
|
{
|
|
const TSet<FEventGraphSamplePtr>& OuterStack = OuterIt.Value();
|
|
const FEventGraphSamplePtr OuterLeafPtr = OuterIt.Key();
|
|
|
|
for( auto InnerIt = StacksForSelectedLeafs.CreateIterator(); InnerIt; ++InnerIt )
|
|
{
|
|
const TSet<FEventGraphSamplePtr>& InnerStack = InnerIt.Value();
|
|
const FEventGraphSamplePtr InnerLeafPtr = InnerIt.Key();
|
|
|
|
// The same roots, so ignore.
|
|
if( InnerLeafPtr == OuterLeafPtr )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const bool bRemoveInner = InnerStack.Contains( OuterLeafPtr );
|
|
const bool bRemoveOuter = OuterStack.Contains( InnerLeafPtr );
|
|
|
|
if( bRemoveOuter )
|
|
{
|
|
OuterIt.RemoveCurrent();
|
|
break;
|
|
}
|
|
else if( bRemoveInner )
|
|
{
|
|
InnerIt.RemoveCurrent();
|
|
}
|
|
}
|
|
}
|
|
|
|
TArray<FEventGraphSamplePtr> UniqueLeafs;
|
|
StacksForSelectedLeafs.GenerateKeyArray( UniqueLeafs );
|
|
|
|
// Store current operation.
|
|
SaveCurrentEventGraphState();
|
|
FEventGraphState* Op = GetCurrentState()->CreateCopyWithNewRoot( UniqueLeafs );
|
|
CurrentStateIndex = EventGraphStatesHistory.Insert( MakeShareable(Op), CurrentStateIndex+1 );
|
|
RestoreEventGraphStateFrom( GetCurrentState() );
|
|
}
|
|
|
|
bool SEventGraph::SetRoot_CanExecute() const
|
|
{
|
|
const int32 NumItemsSelected = TreeView_Base->GetNumItemsSelected();
|
|
return NumItemsSelected > 0 && NumItemsSelected < 16;
|
|
}
|
|
|
|
void SEventGraph::ClearHistory_Execute()
|
|
{
|
|
// Remove all history from the currently visible event graph, but leave the default state.
|
|
const FEventGraphStateRef EventGraphState = GetCurrentState();
|
|
|
|
for( int32 Nx = 0; Nx < EventGraphStatesHistory.Num(); ++Nx )
|
|
{
|
|
const FEventGraphStateRef It = EventGraphStatesHistory[Nx];
|
|
if( It->MaximumEventGraph == EventGraphState->MaximumEventGraph && It->HistoryType != EEventHistoryTypes::NewEventGraph )
|
|
{
|
|
EventGraphStatesHistory.RemoveAt( Nx, EAllowShrinking::No);
|
|
Nx--;
|
|
}
|
|
}
|
|
|
|
// Find new index of the current state.
|
|
for( int32 Nx = 0; Nx < EventGraphStatesHistory.Num(); ++Nx )
|
|
{
|
|
const FEventGraphStateRef It = EventGraphStatesHistory[Nx];
|
|
if( It->MaximumEventGraph == EventGraphState->MaximumEventGraph )
|
|
{
|
|
CurrentStateIndex = Nx;
|
|
break;
|
|
}
|
|
}
|
|
|
|
RestoreEventGraphStateFrom( GetCurrentState() );
|
|
}
|
|
|
|
bool SEventGraph::ClearHistory_CanExecute() const
|
|
{
|
|
bool bCanExecute = false;
|
|
const FEventGraphStateRef EventGraphState = GetCurrentState();
|
|
|
|
for( int32 Nx = 0; Nx < EventGraphStatesHistory.Num(); ++Nx )
|
|
{
|
|
const FEventGraphStateRef It = EventGraphStatesHistory[Nx];
|
|
if( It->MaximumEventGraph == EventGraphState->MaximumEventGraph && It->HistoryType != EEventHistoryTypes::NewEventGraph )
|
|
{
|
|
bCanExecute = true;
|
|
}
|
|
}
|
|
return bCanExecute;
|
|
}
|
|
|
|
void SEventGraph::FilterOutByProperty_Execute( const FEventGraphSamplePtr EventPtr, const FName PropertyName, const bool bReset )
|
|
{
|
|
PROFILER_SCOPE_LOG_TIME( TEXT( "SEventGraph::FilterOutByProperty_Execute" ), nullptr );
|
|
|
|
// Store current operation.
|
|
SaveCurrentEventGraphState();
|
|
FEventGraphState* Op = GetCurrentState()->CreateCopyWithFiltering( PropertyName, EventPtr );
|
|
CurrentStateIndex = EventGraphStatesHistory.Insert( MakeShareable(Op), CurrentStateIndex+1 );
|
|
RestoreEventGraphStateFrom( GetCurrentState() );
|
|
}
|
|
|
|
bool SEventGraph::FilterOutByProperty_CanExecute( const FEventGraphSamplePtr EventPtr, const FName PropertyName, const bool bReset ) const
|
|
{
|
|
const FEventGraphColumn& Column = TreeViewHeaderColumns.FindChecked(PropertyName);
|
|
if( bReset )
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
return EventPtr.IsValid() && Column.bCanBeFiltered;
|
|
}
|
|
}
|
|
|
|
void SEventGraph::CullByProperty_Execute( const FEventGraphSamplePtr EventPtr, const FName PropertyName, const bool bReset )
|
|
{
|
|
PROFILER_SCOPE_LOG_TIME( TEXT( "SEventGraph::CullByProperty_Execute" ), nullptr );
|
|
|
|
// Store current operation.
|
|
SaveCurrentEventGraphState();
|
|
FEventGraphState* Op = GetCurrentState()->CreateCopyWithCulling( PropertyName, EventPtr );
|
|
CurrentStateIndex = EventGraphStatesHistory.Insert( MakeShareable(Op), CurrentStateIndex+1 );
|
|
RestoreEventGraphStateFrom( GetCurrentState() );
|
|
}
|
|
|
|
bool SEventGraph::CullByProperty_CanExecute( const FEventGraphSamplePtr EventPtr, const FName PropertyName, const bool bReset ) const
|
|
{
|
|
const FEventGraphColumn& Column = TreeViewHeaderColumns.FindChecked(PropertyName);
|
|
if( bReset )
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
return EventPtr.IsValid() && Column.bCanBeCulled;
|
|
}
|
|
}
|
|
|
|
void SEventGraph::GetEventsForChangingExpansion( TArray<FEventGraphSamplePtr>& out_Events, const ESelectedEventTypes::Type SelectedEventType )
|
|
{
|
|
if( SelectedEventType == ESelectedEventTypes::AllEvents )
|
|
{
|
|
out_Events = GetCurrentState()->GetRealRoot()->GetChildren();
|
|
}
|
|
else if( SelectedEventType == ESelectedEventTypes::SelectedEvents )
|
|
{
|
|
out_Events = TreeView_Base->GetSelectedItems();
|
|
}
|
|
else if( SelectedEventType == ESelectedEventTypes::SelectedThreadEvents )
|
|
{
|
|
TArray<FEventGraphSamplePtr> SelectedItems = TreeView_Base->GetSelectedItems();
|
|
TSet<FEventGraphSamplePtr> ThreadEventSet;
|
|
|
|
for( int32 EventIndex = 0; EventIndex < SelectedItems.Num(); EventIndex++ )
|
|
{
|
|
const FEventGraphSamplePtr ThreadEvent = SelectedItems[EventIndex]->GetOutermost();
|
|
ThreadEventSet.Add( ThreadEvent );
|
|
}
|
|
|
|
out_Events = ThreadEventSet.Array();
|
|
}
|
|
}
|
|
|
|
void SEventGraph::SetExpansionForEvents_Execute( const ESelectedEventTypes::Type SelectedEventType, bool bShouldExpand )
|
|
{
|
|
TArray<FEventGraphSamplePtr> Events;
|
|
GetEventsForChangingExpansion( Events, SelectedEventType );
|
|
TreeView_SetItemsExpansion_Recurrent( Events, bShouldExpand );
|
|
}
|
|
|
|
bool SEventGraph::SetExpansionForEvents_CanExecute( const ESelectedEventTypes::Type SelectedEventType, bool bShouldExpand ) const
|
|
{
|
|
const int32 NumSelectedItems = TreeView_Base->GetNumItemsSelected();
|
|
if( SelectedEventType == ESelectedEventTypes::AllEvents )
|
|
{
|
|
return GetCurrentStateViewMode() == EEventGraphViewModes::Hierarchical;
|
|
}
|
|
else if( SelectedEventType == ESelectedEventTypes::SelectedEvents )
|
|
{
|
|
return GetCurrentStateViewMode() == EEventGraphViewModes::Hierarchical && NumSelectedItems > 0;
|
|
}
|
|
else if( SelectedEventType == ESelectedEventTypes::SelectedThreadEvents )
|
|
{
|
|
return GetCurrentStateViewMode() == EEventGraphViewModes::Hierarchical && NumSelectedItems > 0;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void SEventGraph::Map_SelectAllFrames_Global()
|
|
{
|
|
TSharedRef<FUICommandList> ProfilerCommandList = FProfilerManager::Get()->GetCommandList();
|
|
const FProfilerCommands& ProfilerCommands = FProfilerManager::GetCommands();
|
|
const FProfilerActionManager& ProfilerActionManager = FProfilerManager::GetActionManager();
|
|
|
|
// Assumes only one instance of the event graph, this may change in future.
|
|
const FUIAction* UIAction = ProfilerCommandList->GetActionForCommand( ProfilerCommands.EventGraph_SelectAllFrames );
|
|
|
|
if( !UIAction )
|
|
{
|
|
ProfilerCommandList->MapAction( ProfilerCommands.EventGraph_SelectAllFrames, SelectAllFrames_Custom() );
|
|
}
|
|
else
|
|
{
|
|
// Replace with the new UI Action.
|
|
const FUIAction NewUIAction = SelectAllFrames_Custom();
|
|
new((void*)UIAction) FUIAction(NewUIAction);
|
|
}
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
Settings
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
EVisibility SEventGraph::EventGraphViewMode_GetVisibility( const EEventGraphViewModes::Type ViewMode ) const
|
|
{
|
|
if( ViewMode == EEventGraphViewModes::FlatInclusiveCoalesced || ViewMode == EEventGraphViewModes::FlatExclusiveCoalesced )
|
|
{
|
|
const EVisibility Vis = FProfilerManager::GetSettings().bShowCoalescedViewModesInEventGraph ? EVisibility::Visible : EVisibility::Collapsed;
|
|
if (Vis == EVisibility::Collapsed && GetCurrentStateViewMode() == ViewMode)
|
|
{
|
|
// If view mode is not available event graph will switch to the hierarchical view mode.
|
|
SEventGraph* MutableThis = const_cast< SEventGraph* >( this );
|
|
MutableThis->EventGraphViewMode_OnCheckStateChanged( ECheckBoxState::Checked, EEventGraphViewModes::Hierarchical );
|
|
}
|
|
return Vis;
|
|
}
|
|
else
|
|
{
|
|
return EVisibility::Visible;
|
|
}
|
|
}
|
|
|
|
void SEventGraph::ExpandCulledEvents( FEventGraphSamplePtr EventPtr )
|
|
{
|
|
// Update not culled children.
|
|
EventPtr->SetBooleanStateForAllChildren<EEventPropertyIndex::bIsCulled>(false);
|
|
EventPtr->SetBooleanStateForAllChildren<EEventPropertyIndex::bNeedNotCulledChildrenUpdate>(true);
|
|
|
|
struct FAddExcludedCulledEvents
|
|
{
|
|
FORCEINLINE void operator()( FEventGraphSample* InEventPtr, TArray<FEventGraphSamplePtr>& out_ExpandedCulledEvents )
|
|
{
|
|
out_ExpandedCulledEvents.Add( InEventPtr->AsShared() );
|
|
}
|
|
};
|
|
EventPtr->ExecuteOperationForAllChildren( FAddExcludedCulledEvents(), GetCurrentState()->ExpandedCulledEvents );
|
|
|
|
CreateEvents();
|
|
TreeView_Refresh();
|
|
}
|
|
|
|
void SEventGraph::SelectAllFrames_Execute()
|
|
{
|
|
SwitchToEventGraphState( 0 );
|
|
}
|
|
|
|
bool SEventGraph::SelectAllFrames_CanExecute() const
|
|
{
|
|
return IsEventGraphStatesHistoryValid();
|
|
}
|
|
|
|
void SEventGraph::ProfilerManager_OnViewModeChanged( EProfilerViewMode NewViewMode )
|
|
{
|
|
// if( NewViewMode == EProfilerViewMode::LineIndexBased )
|
|
// {
|
|
// FunctionDetailsBox->SetVisibility( EVisibility::Visible );
|
|
// FunctionDetailsBox->SetEnabled( true );
|
|
// }
|
|
// else if( NewViewMode == EProfilerViewMode::ThreadViewTimeBased )
|
|
// {
|
|
// FunctionDetailsBox->SetVisibility( EVisibility::Collapsed );
|
|
// FunctionDetailsBox->SetEnabled( false );
|
|
// }
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
EventGraphState
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
#define LOCTEXT_NAMESPACE "FEventGraphState"
|
|
|
|
FText SEventGraph::FEventGraphState::GetFullDescription() const
|
|
{
|
|
FTextBuilder Builder;
|
|
|
|
FFormatNamedArguments Args;
|
|
Args.Add(TEXT("FrameStartIndex"), GetEventGraph()->GetFrameStartIndex());
|
|
Args.Add(TEXT("FrameEndIndex"), GetEventGraph()->GetFrameEndIndex());
|
|
Args.Add(TEXT("NumberOfFrames"), GetNumFrames());
|
|
Builder.AppendLineFormat(LOCTEXT("FullDesc", "Event graph with range ({FrameStartIndex},{FrameEndIndex}) contains {NumberOfFrames} frame(s)"), Args);
|
|
|
|
Builder.Indent();
|
|
|
|
if( IsRooted() )
|
|
{
|
|
Builder.AppendLine(GetRootedDesc());
|
|
}
|
|
|
|
if( IsCulled() )
|
|
{
|
|
Builder.AppendLine(GetCullingDesc());
|
|
}
|
|
|
|
if( IsFiltered() )
|
|
{
|
|
Builder.AppendLine(GetFilteringDesc());
|
|
}
|
|
|
|
return Builder.ToText();
|
|
}
|
|
|
|
FText SEventGraph::FEventGraphState::GetRootedDesc() const
|
|
{
|
|
const int32 NumFakeRoots = FakeRoot->GetChildren().Num();
|
|
|
|
if (NumFakeRoots == 1)
|
|
{
|
|
FFormatNamedArguments Args;
|
|
Args.Add(TEXT("StatName"), FText::FromName(FakeRoot->GetChildren()[0]->_StatName));
|
|
return FText::Format(LOCTEXT("RootedDesc_SingleChild", "Rooted: {StatName}"), Args);
|
|
}
|
|
|
|
return LOCTEXT("RootedDesc_MultipleChildren", "Rooted: Multiple");
|
|
}
|
|
|
|
FText SEventGraph::FEventGraphState::GetCullingDesc() const
|
|
{
|
|
FFormatNamedArguments Args;
|
|
Args.Add(TEXT("CulledPropertyName"), FText::FromName(CullPropertyName));
|
|
Args.Add(TEXT("EventName"), FText::FromString(CullEventPtr->GetFormattedValue(FEventGraphSample::GetEventPropertyByName(CullPropertyName).Index)));
|
|
|
|
return FText::Format(LOCTEXT("CulledDesc", "Culled: {CulledPropertyName} {EventName}"), Args);
|
|
}
|
|
|
|
FText SEventGraph::FEventGraphState::GetFilteringDesc() const
|
|
{
|
|
FFormatNamedArguments Args;
|
|
Args.Add(TEXT("FilterPropertyName"), FText::FromName(FilterPropertyName));
|
|
Args.Add(TEXT("EventName"), FText::FromString(FilterEventPtr->GetFormattedValue(FEventGraphSample::GetEventPropertyByName(FilterPropertyName).Index)));
|
|
|
|
return FText::Format(LOCTEXT("FilteredDesc", "Filtered: {FilterPropertyName} {EventName}"), Args);
|
|
}
|
|
|
|
FText SEventGraph::FEventGraphState::GetHistoryDesc() const
|
|
{
|
|
FText Result = LOCTEXT("DefaultDesc", "Default state");
|
|
|
|
if( HistoryType == EEventHistoryTypes::Rooted )
|
|
{
|
|
Result = GetRootedDesc();
|
|
}
|
|
else if( HistoryType == EEventHistoryTypes::Culled )
|
|
{
|
|
Result = GetCullingDesc();
|
|
}
|
|
else if( HistoryType == EEventHistoryTypes::Filtered )
|
|
{
|
|
Result = GetFilteringDesc();
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
static void CreateOneToOneMapping_EventGraphSample
|
|
(
|
|
const FEventGraphSamplePtr LocalEvent,
|
|
const FEventGraphSamplePtr SourceEvent,
|
|
TMap< FEventGraphSamplePtr, FEventGraphSamplePtr >& out_MaximumToAverageMapping,
|
|
TMap< FEventGraphSamplePtr, FEventGraphSamplePtr >& out_AverageToMaximumMapping
|
|
)
|
|
{
|
|
out_MaximumToAverageMapping.Add( LocalEvent, SourceEvent );
|
|
out_AverageToMaximumMapping.Add( SourceEvent, LocalEvent );
|
|
|
|
check( LocalEvent->GetChildren().Num() == SourceEvent->GetChildren().Num() );
|
|
for( int32 Index = 0; Index < LocalEvent->GetChildren().Num(); ++Index )
|
|
{
|
|
CreateOneToOneMapping_EventGraphSample( LocalEvent->GetChildren()[Index], SourceEvent->GetChildren()[Index], out_MaximumToAverageMapping, out_AverageToMaximumMapping );
|
|
}
|
|
}
|
|
|
|
void SEventGraph::FEventGraphState::CreateOneToOneMapping()
|
|
{
|
|
CreateOneToOneMapping_EventGraphSample( MaximumEventGraph->GetRoot(), AverageEventGraph->GetRoot(), MaximumToAverageMapping, AverageToMaximumMapping );
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|
|
|
|
#endif // STATS
|