本文主要针对阅读UE4源码中Profiler工具的相关的源码记录的笔记。
源码版本: UE 4.20
源码路径: Epic Games\Engine\UE_4.20\Engine\Source\Developer\Profiler\Public
目录结构:
- Public
- ProfilerCommon.h
- ProfilerModule.h
- Private
- ProfilerModule.cpp
- ProfilerManager.cpp
- ProfilerSession.cpp
- ProfilerCommands.cpp
- Widgets
- SProfilerWindow.cpp
- SProfilerToolbar.cpp
- SProfilerMiniView.cpp
- SMultiDumpBrowser.cpp
- SFiltersAndPresets.cpp
- SProfilerGraphPanel.cpp
- SDataGraph.cpp
- SEventGraph.cpp
- SProfilerThreadView.cpp // TODO
ProfilerModule
- CreateProfilerWindow
- FProfilerManager::Initialize(SessionManager)
- SNew( SProfilerWindow )
- FProfilerManager->AssignProfilerWindow ( ProfilerWindow )
- ShutdownModule
- FProfilerManager->Shutdown()
- StatsMemoryDumpCommand // TODO
ProfilerModule::CreateProfilerWindow函数来模块的入口, 唯一的调用者为:
SSessionFrontend.cpp line 207
else if (TabIdentifier == ProfilerTabId)
{
IProfilerModule& ProfilerModule = FModuleManager::LoadModuleChecked(TEXT("Profiler"));
TabWidget = ProfilerModule.CreateProfilerWindow(SessionManager.ToSharedRef(), DockTab);
即打开Profiler窗口时, 传入的SessionManager, 包含了当前载入的会话情况(可能是live, 可能是打开的Stats文件等情况)
ProfilerWindow
主窗口, 继承于 SCompoundWidget
Construct
布局结构:
- SOverlay
- SVerticalBox
- SHorizontalBox(autoHeight)
- SProfilerToolbar
- SBox
- SHorizontalBox
- SProfilerMiniView
- SHorizontalBox
- SSpitter (horizontal )
- SSpitter(0.25 Vertical)
- SVerticalBox(0.25)
- SHorizontalBox
- SImage
- STextBlock("Stats dump browser")
- MultiDumpBrowser
- SHorizontalBox
- SVerticalBox(0.75)
- SHorizontalBox
- SImage
- STextBlock("Filters And Presets")
- SFiltersAndPresets
- SHorizontalBox
- SVerticalBox(0.25)
- SSplitter(0.75 Vertical)
- SVerticalBox(0.25)
- SHorizontalBox
- SImage
- STextBlock("Graph View")
- SProfilerGraphPanel
- SHorizontalBox
- SVerticalBox(EventGraphPanel) (0.75)
- SVerticalBox(0.25)
- SSpitter(0.25 Vertical)
- SHorizontalBox(autoHeight)
- SBorder
- STextBlock ( Please select a session from the Session Brower or load a saved capture )
- SNotificationList
- Expose OverlaySettingsSlot
- SVerticalBox
截图:
主要内置Widget:
- SOverlay 框架布局
- SVerticalBox 竖向线性布局
- SHorizontalBox 横向线性布局
- SSpitter 按比例分隔布局
- SImage 图片控件
- STextBlock 文本控件
自定义Widget:
- ProfilerToolbar 工具栏
- ProfilerMiniView
- SMultiDumpBrowser
- SFiltersAndPresets
- SProfilerGraphPanel
- EventGraphPanel
关键属性:
- AutoWidth 宽度自适应
- AutoHeight 高度自适应
- FillWidth(1.0f) 宽度填充为父元素大小
- FillHeight(1.0f) 高度填充父元素剩余最大
- Padding(left, top, right, bottom) 内边距
- HAlign(HAlign_Fill) 横向对齐方式(铺满)
- VAlign(VAlign_Fill) 纵向对齐方式(铺满)
- Orientation(Orient_Vertical) 方向
- Visibility 可见性
- IsEnabled // TODO
- Expose // TODO
ProfilerToolbar
工具栏, 继承于 SCompoundWidget
Construct
布局:
- SHorizontalBox
- SBorder
- FToolbarBuilder.MakeWidget()
- SBorder
创建Command:
/** UI_COMMAND takes long for the compile to optimize */
PRAGMA_DISABLE_OPTIMIZATION
void FProfilerCommands::RegisterCommands()
{
/*-----------------------------------------------------------------------------
Global and custom commands.
-----------------------------------------------------------------------------*/
UI_COMMAND( ToggleDataPreview, "Data Preview", "Toggles the data preview", EUserInterfaceActionType::ToggleButton, FInputChord( EModifierKey::Control, EKeys::R ) );
UI_COMMAND( ToggleDataCapture, "Data Capture", "Toggles the data capture", EUserInterfaceActionType::ToggleButton, FInputChord( EModifierKey::Control, EKeys::C ) );
UI_COMMAND( ToggleShowDataGraph, "Show Data Graph", "Toggles showing all data graphs", EUserInterfaceActionType::ToggleButton, FInputChord() );
UI_COMMAND( OpenEventGraph, "Open Event Graph", "Opens a new event graph", EUserInterfaceActionType::Button, FInputChord() );
}
菜单点击回调函数:
/*-----------------------------------------------------------------------------
ToggleDataPreview
\-----------------------------------------------------------------------------*/
void FProfilerActionManager::Map_ToggleDataPreview_Global()
{
This->CommandList->MapAction( This->GetCommands().ToggleDataPreview, ToggleDataPreview_Custom( FGuid() ) );
}
const FUIAction FProfilerActionManager::ToggleDataPreview_Custom( const FGuid SessionInstanceID ) const
{
FUIAction UIAction;
UIAction.ExecuteAction = FExecuteAction::CreateRaw( this, &FProfilerActionManager::ToggleDataPreview_Execute, SessionInstanceID );
UIAction.CanExecuteAction = FCanExecuteAction::CreateRaw( this, &FProfilerActionManager::ToggleDataPreview_CanExecute, SessionInstanceID );
UIAction.GetActionCheckState = FGetActionCheckState::CreateRaw( this, &FProfilerActionManager::ToggleDataPreview_GetCheckState, SessionInstanceID );
return UIAction;
}
void FProfilerActionManager::ToggleDataPreview_Execute( const FGuid SessionInstanceID )
{
const bool bDataPreviewing = !This->IsDataPreviewing();
This->SetDataPreview( bDataPreviewing );
if (!bDataPreviewing)
{
This->bLivePreview = false;
}
}
bool FProfilerActionManager::ToggleDataPreview_CanExecute( const FGuid SessionInstanceID ) const
{
const bool bCanExecute = This->ActiveSession.IsValid() && This->ProfilerType == EProfilerSessionTypes::Live && This->ActiveInstanceID.IsValid();
return bCanExecute;
}
ECheckBoxState FProfilerActionManager::ToggleDataPreview_GetCheckState( const FGuid SessionInstanceID ) const
{
const bool bDataPreview = This->IsDataPreviewing();
return bDataPreview ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
}
创建工具栏:
struct Local
{
static void FillToolbar(FToolBarBuilder& ToolbarBuilder)
{
ToolbarBuilder.BeginSection("File");
{
ToolbarBuilder.AddToolBarButton(FProfilerCommands::Get().ProfilerManager_Load);
ToolbarBuilder.AddToolBarButton(FProfilerCommands::Get().ProfilerManager_LoadMultiple);
ToolbarBuilder.AddToolBarButton(FProfilerCommands::Get().ProfilerManager_Save);
}
ToolbarBuilder.EndSection();
ToolbarBuilder.BeginSection("Capture");
{
ToolbarBuilder.AddToolBarButton(FProfilerCommands::Get().ToggleDataPreview);
ToolbarBuilder.AddToolBarButton(FProfilerCommands::Get().ProfilerManager_ToggleLivePreview);
ToolbarBuilder.AddToolBarButton(FProfilerCommands::Get().ToggleDataCapture);
}
ToolbarBuilder.EndSection();
ToolbarBuilder.BeginSection("Profilers");
{
ToolbarBuilder.AddToolBarButton(FProfilerCommands::Get().StatsProfiler);
//ToolbarBuilder.AddToolBarButton(FProfilerCommands::Get().MemoryProfiler);
ToolbarBuilder.AddToolBarButton(FProfilerCommands::Get().FPSChart);
}
ToolbarBuilder.EndSection();
ToolbarBuilder.BeginSection("Options");
{
ToolbarBuilder.AddToolBarButton(FProfilerCommands::Get().OpenSettings);
}
ToolbarBuilder.EndSection();
}
};
TSharedPtr ProfilerCommandList = FProfilerManager::Get()->GetCommandList();
FToolBarBuilder ToolbarBuilder(ProfilerCommandList.ToSharedRef(), FMultiBoxCustomization::None);
Local::FillToolbar(ToolbarBuilder);
按钮样式:
注意按钮的样式是在 UE_4.20\Engine\Source\Editor\EditorStyle\Private\SlateEditorStyle.cpp 统一设置的:
Set( "ProfilerCommand.ToggleDataPreview", new IMAGE_BRUSH( "Icons/Profiler/profiler_sync_40x", Icon40x40 ) );
Set( "ProfilerCommand.ToggleDataPreview.Small", new IMAGE_BRUSH( "Icons/Profiler/profiler_sync_40x", Icon20x20 ) );
ProfilerMiniView
Widget used to present thread data in the mini-view., 继承于 SCompoundWidget
Tick
检查Geometry是否变化了(大小, 尺寸), 则置 bUpdateData = true
如果bUpdateData为true, 则调用 ProcessData() 来重新处理数据
OnPaint
类似于其他框架的 OnDraw,
传入的参数:
- Args All the arguments necessary to paint this widget
- AllottedGeometry 大小 The FGeometry that describes an area in which the widget should appear.
- MyCullingRect The rectangle representing the bounds currently being used to completely cull widgets. Unless IsChildWidgetCulled(...) returns true, you should paint the widget.
- OutDrawElements A list of FDrawElements to populate with the output.
- LayerId (层的ID, 越大越靠近用户)
- InWidgetStyle Color and Opacity to be applied to all the descendants of the widget being painted
- bParentEnabled True if the parent of this widget is enabled.父元素是否enable
画图的函数:
- FSlateDrawElement::MakeBox 绘制矩形
- FSlateDrawElement::MakeText 绘制文字
MakeBox参数:
- ElementList The list in which to add elements
- InLayer The layer to draw the element on
- PaintGeometry DrawSpace position and dimensions; see FPaintGeometry
- InBrush Brush to apply to this element
- InClippingRect Parts of the element are clipped if it falls outside of this rectangle
- InDrawEffects Optional draw effects to apply
- InTint Color to tint the element
这里的PaintGenmetry都是基于父元素的:
AllottedGeometry.ToPaintGeometry( FVector2D( 0, 0 ), FVector2D( MiniViewSizeX, AllottedGeometry.Size.Y ) ),
第一个FVector为offset, 第二个FVector为size
x, y的坐标系是:
-----------> (x 轴)
|
|
|
v
(y 轴)
鼠标事件 // TODO
Drop的事件因为涉及到很多计算, 暂时略过, 基本思路和其他框架的处理差不多, onMouseDown 开始处理, onMouseMove 开始移动, onMouseUp 结束移动.
MultiDumpBrowser
A custom widget used to display a histogram. 继承于 SCompoundWidget
Construct
布局:
- SOverlay
- SVerticalBox
- SBorder
- SHorizontalBox
- STextBlock ("Show thread totals for:")
- SEditableTextBox
- SHorizontalBox
- SBorder
- SBorder
- SListView(每行元素就是一个 STextBlock, 显示一个文字)
- SVerticalBox
ListView
创建ListView:
SAssignNew(FileList, SListView>
.ItemHeight(16)
.ListItemsSource(&StatsFiles)
.OnGenerateRow(this, &SMultiDumpBrowser::GenerateFileRow)
.OnSelectionChanged(this, &SMultiDumpBrowser::SelectionChanged)
数据源: ListItemsSource
创建每一列的View: OnGenerateRow
点击选中回调函数: OnSelectionChanged
TSharedRef SMultiDumpBrowser::GenerateFileRow(TSharedPtr FileItem, const TSharedRef& OwnerTable)
{
return SNew(SFileTableRow, OwnerTable, FileItem);
}
class SFileTableRow : public STableRow>
{
void Construct(const FArguments& InArgs, const TSharedRef& OwnerTable, const TSharedPtr& InFileDesc)
{
STableRow>::Construct(STableRow>::FArguments(), OwnerTable);
Desc = InFileDesc;
ChildSlot
[
SNew(SBox)
[
SNew(STextBlock)
.Text(this, &SFileTableRow::GetDisplayName)
]
];
}
}
每一行的Widget需要继承于SFileTableRow即可.
FiltersAndPresets
Configurable window with advanced options for filtering and creating presets.. 继承于 SCompoundWidget
Construct
布局:
- SVerticalBox
- SBorder
- SVerticalBox
- SSearchBox
- SVerticalBox
- SHorizontalBox
- STextBlock("Group by")
- SComboBox
- SHorizontalBox
- STextBlock("Sort by")
- SComboBox
- SHorizontalBox
- SCheckBox
- SHorizontalBox
- SImage
- STextBlock("HierarchicalTime")
- SHorizontalBox
- SCheckBox("NumberFloat")
- SCheckBox("NumberInt")
- SCheckBox("Memory")
- SCheckBox
- SHorizontalBox
- SBorder
- STreeView ( gruops tree )
- SVerticalBox
- SBorder
截图:
STreeView
SAssignNew( GroupAndStatTree, STreeView< FGroupOrStatNodePtr > )
.SelectionMode(ESelectionMode::Single) // 单选模式
.TreeItemsSource( &FilteredGroupNodes ) // 数据源
.OnGetChildren( this, &SFiltersAndPresets::GroupAndStatTree_OnGetChildren ) // 获取树的子元素回调函数
.OnGenerateRow( this, &SFiltersAndPresets::GroupAndStatTree_OnGenerateRow ) // 生成每一列回调函数
.OnMouseButtonDoubleClick( this, &SFiltersAndPresets::GroupAndStatTree_OnMouseButtonDoubleClick ) // 鼠标双击回调函数
//.OnSelectionChanged( this, &SFiltersAndPresets::GroupAndStatTree_OnSelectionChanged )
.ItemHeight( 12 )
创建子元素
TSharedRef< ITableRow > SFiltersAndPresets::GroupAndStatTree_OnGenerateRow( FGroupOrStatNodePtr GroupOrStatNode, const TSharedRef< STableViewBase >& OwnerTable )
{
TSharedRef< ITableRow > TableRow =
SNew(SGroupAndStatTableRow, OwnerTable, GroupOrStatNode.ToSharedRef())
.OnShouldBeEnabled( this, &SFiltersAndPresets::GroupAndStatTableRow_ShouldBeEnabled )
.HighlightText( this, &SFiltersAndPresets::GroupAndStatTableRow_GetHighlightText );
return TableRow;
}
子元素必须继承于 STableRow
/** Widget that represents a table row in the groups and stats' tree control. Generates widgets for each column on demand. */
class SGroupAndStatTableRow : public STableRow< FGroupOrStatNodePtr >
{
void Construct( const FArguments& InArgs, const TSharedRef& InOwnerTableView, const TSharedRef& InGroupOrStatNode )
{
}
}
子元素布局:
- ChildSlot
- SHorizontalBox
- SExpanderArrow ( Expander arrow)
- SImage ( Icon to visualize group or stat type)
- STextBlock (description text)
- SIMage (tooltip hit icon )
- SHorizontalBox
SExpanderArrow展开的箭头, 支持按层级缩进., 这是一个Slate库里公共的类, 路径在:
Epic Games\Engine\UE_4.20\Engine\Source\Runtime\Slate\Public\Widgets\Views\SExpanderArrow.h
ProfilerGraphPanel
A custom widget that acts as a container for widgets like SDataGraph or SEventTree., 继承于SCompoundWidget.
Construct
布局:
- SBorder
- SHorizontalBox
- SVerticalBox
- SDataGraph
- SProfilerThreadView
- SScrollBar (horizontal )
- SScrollBar ( vertical )
- SVerticalBox
- SHorizontalBox
SDataGraph
A custom widget used to display graphs., 继承于SCompoundWidget
TODO: 具体计算没细看, 主要是计算每一个points, 然后调用 MakeLines 传入points, 则会自动把这些点连着绘制成一条线
截图:
SEventGraph
A custom event graph widget used to visualize profiling data. 继承于SCompoundWidget.
Construct
布局:
- Splitter
- SVerticalBox(0.5)
- SBorder
- SVerticalBox
- SHorizontalBox
- EventGraphTypes Widget
- EventGraphViewModes Widget
- BoxForOptions Widget
- SHorizontalBox
- ThreadFilter Widget
- SBox (FunctionDetailsBox
- SBorder
- SHorizontalBox
- VerticalBox ( calling functions )
- SHorizontalBox
- VerticalBox ( current unctions )
- SHorizontalBox
- VerticalBox ( called functions )
- SHorizontalBox
- SBorder
- SHorizontalBox
- SVerticalBox
- SBorder
- SBorder(0.5)
- SHorizontalBox
- STreeView
- SBox
- ExternalScrollbar
- SHorizontalBox
- SVerticalBox(0.5)
截图:
TreeView
SAssignNew(TreeView_Base, STreeView)
.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))
.ItemHeight(12.0f)
.HeaderRow
(
SAssignNew(TreeViewHeaderRow,SHeaderRow)
.Visibility(EVisibility::Visible)
)
InitializeAndShowHeaderColumns();
class SEventGraphTableRow : public SMultiColumnTableRow < FEventGraphSamplePtr >
ProfilerSession
session type:
- live
- StatsFile
- StatsFileRaw
- ...
NOTE ATTRIBUTES
Created Date: 2018-08-20 06:28:05
Last Evernote Update Date: 2018-08-22 08:38:21