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

創(chuàng)建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;
}

創(chuàng)建工具欄:

       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 統(tǒng)一設置的:

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

創(chuàng)建ListView:

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

數據源: ListItemsSource
創(chuàng)建每一列的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 )

創(chuàng)建子元素

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

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市申窘,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件陪踩,死亡現(xiàn)場離奇詭異杖们,居然都是意外死亡,警方通過查閱死者的電腦和手機肩狂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進店門摘完,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人傻谁,你說我怎么就攤上這事孝治。” “怎么了审磁?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵谈飒,是天一觀的道長。 經常有香客問我态蒂,道長杭措,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任钾恢,我火速辦了婚禮手素,結果婚禮上,老公的妹妹穿的比我還像新娘瘩蚪。我一直安慰自己泉懦,他們只是感情好,可當我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布疹瘦。 她就那樣靜靜地躺著崩哩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪拱礁。 梳的紋絲不亂的頭發(fā)上琢锋,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天,我揣著相機與錄音呢灶,去河邊找鬼吴超。 笑死,一個胖子當著我的面吹牛鸯乃,可吹牛的內容都是我干的鲸阻。 我是一名探鬼主播,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼缨睡,長吁一口氣:“原來是場噩夢啊……” “哼鸟悴!你這毒婦竟也來了?” 一聲冷哼從身側響起奖年,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤细诸,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后陋守,有當地人在樹林里發(fā)現(xiàn)了一具尸體震贵,經...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡利赋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了猩系。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片媚送。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖寇甸,靈堂內的尸體忽然破棺而出塘偎,到底是詐尸還是另有隱情,我是刑警寧澤拿霉,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布吟秩,位于F島的核電站,受9級特大地震影響友浸,放射性物質發(fā)生泄漏峰尝。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一收恢、第九天 我趴在偏房一處隱蔽的房頂上張望武学。 院中可真熱鬧,春花似錦伦意、人聲如沸火窒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽熏矿。三九已至,卻和暖如春离钝,著一層夾襖步出監(jiān)牢的瞬間票编,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工卵渴, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留慧域,地道東北人。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓浪读,卻偏偏與公主長得像昔榴,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子碘橘,可洞房花燭夜當晚...
    茶點故事閱讀 45,066評論 2 355