UE4源码阅读-Profiler

本文主要针对阅读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<IProfilerModule>(TEXT("Profiler"));
              TabWidget = ProfilerModule.CreateProfilerWindow(SessionManager.ToSharedRef(), DockTab);

即打开Profiler窗口时, 传入的SessionManager, 包含了当前载入的会话情况(可能是live, 可能是打开的Stats文件等情况)

ProfilerWindow

主窗口, 继承于 SCompoundWidget

Construct

布局结构:

  • SOverlay
    • SVerticalBox
      • SHorizontalBox(autoHeight)
        • SProfilerToolbar
      • SBox
        • SHorizontalBox
          • SProfilerMiniView
      • SSpitter (horizontal )
        • SSpitter(0.25 Vertical)
          • SVerticalBox(0.25)
            • SHorizontalBox
              • SImage
              • STextBlock("Stats dump browser")
            • MultiDumpBrowser
          • SVerticalBox(0.75)
            • SHorizontalBox
              • SImage
              • STextBlock("Filters And Presets")
            • SFiltersAndPresets
        • SSplitter(0.75 Vertical)
          • SVerticalBox(0.25)
            • SHorizontalBox
              • SImage
              • STextBlock("Graph View")
            • SProfilerGraphPanel
          • SVerticalBox(EventGraphPanel) (0.75)
    • SBorder
      • STextBlock ( Please select a session from the Session Brower or load a saved capture )
    • SNotificationList
    • Expose OverlaySettingsSlot

截图:

2018-08-20_06-28-05.png

主要内置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()

创建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<FUICommandList> 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

2018-08-20_06-28-05-2.png

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
    • SBorder
      • SListView(每行元素就是一个 STextBlock, 显示一个文字)

ListView

创建ListView:

SAssignNew(FileList, SListView<TSharedPtr<FFileDescriptor>>
    .ItemHeight(16)
    .ListItemsSource(&StatsFiles)
    .OnGenerateRow(this, &SMultiDumpBrowser::GenerateFileRow)
    .OnSelectionChanged(this, &SMultiDumpBrowser::SelectionChanged)

数据源: ListItemsSource
创建每一列的View: OnGenerateRow
点击选中回调函数: OnSelectionChanged

TSharedRef<ITableRow> SMultiDumpBrowser::GenerateFileRow(TSharedPtr<FFileDescriptor> FileItem, const TSharedRef<STableViewBase>& OwnerTable)
{
    return SNew(SFileTableRow, OwnerTable, FileItem);
}




class SFileTableRow : public STableRow<TSharedPtr<FFileDescriptor>>
{
              void Construct(const FArguments& InArgs, const TSharedRef<STableViewBase>& OwnerTable, const TSharedPtr<FFileDescriptor>& InFileDesc)
              {
                     STableRow<TSharedPtr<FFileDescriptor>>::Construct(STableRow<TSharedPtr<FFileDescriptor>>::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")
            • SCheckBox("NumberFloat")
            • SCheckBox("NumberInt")
            • SCheckBox("Memory")
        • SBorder
          • STreeView ( gruops tree )

截图:

2018-08-20_06-28-05-3.png

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<STableViewBase>& InOwnerTableView, const TSharedRef<FGroupOrStatNode>& InGroupOrStatNode )
    {

    }
}

子元素布局:

  • ChildSlot
    • SHorizontalBox
      • SExpanderArrow ( Expander arrow)
      • SImage ( Icon to visualize group or stat type)
      • STextBlock (description text)
      • SIMage (tooltip hit icon )

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 )

SDataGraph

A custom widget used to display graphs., 继承于SCompoundWidget

TODO: 具体计算没细看, 主要是计算每一个points, 然后调用 MakeLines 传入points, 则会自动把这些点连着绘制成一条线

截图:

2018-08-20_06-28-05-4.png

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 )
    • SBorder(0.5)
      • SHorizontalBox
        • STreeView
        • SBox
          • ExternalScrollbar

截图:

2018-08-20_06-28-05-5.png

TreeView

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))
.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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349