Files
UnrealEngineUWP/Engine/Source/Programs/MemoryProfiler2/HistogramParser.cs
Ben Marsh 7598af0532 Update copyright notices to 2019.
#rb none
#lockdown Nick.Penwarden

[CL 4662404 by Ben Marsh in Main branch]
2018-12-14 13:41:00 -05:00

761 lines
25 KiB
C#

// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
namespace MemoryProfiler2
{
/// <summary> Histogram parser. </summary>
public static class FHistogramParser
{
/// <summary>
/// The assumption that this constant is 2 is still hardcoded
/// in some places, so increasing the number of banks is not
/// as simple as changing this constant.
///
/// Second memory bank is used only on PS3 to show local memory.
/// </summary>
public const int NUM_MEMORY_BANKS = 2;
/// <summary> Array of histogram bars. </summary>
static public List<FHistogramBar>[] HistogramBars;
/// <summary> Array of histogram bars for selected main histogram bar. </summary>
static private List<FHistogramBar> HistogramSelectionBars = new List<FHistogramBar>();
/// <summary> Currently selected histogram bar from the memory bank. </summary>
static private FHistogramBar SelectedHistogramBar;
/// <summary> Currently selected histogram bar from the detailed bank. </summary>
static private FHistogramBar SubselectedHistogramBar;
/// <summary> Index of the bank for the currently selected histogram bar. </summary>
static private int SelectedMemoryBankIndex = 0;
/// <summary> Indices of the currently selected histogram bars in the memory banks. </summary>
static private int[] SelectedHistogramBarIndex = new int[ 2 ] { 0, 0 };
static private Pen BlackDashedPen = new Pen( Color.Black );
static private Pen RedDashedPen = new Pen( Color.Red );
private static Brush CheckerBWBrush1x1 = new HatchBrush( HatchStyle.Percent50, Color.FromArgb( 224, Color.Plum ), Color.FromArgb( 224, Color.SkyBlue ) );
private static Brush CheckerBWBrush2x2 = new HatchBrush( HatchStyle.SmallCheckerBoard, Color.FromArgb( 192, Color.Plum ), Color.FromArgb( 192, Color.SkyBlue ) );
private static Brush CheckerBWBrush4x4 = new HatchBrush( HatchStyle.LargeCheckerBoard, Color.FromArgb( 160, Color.Plum ), Color.FromArgb( 160, Color.SkyBlue ) );
private static Pen OutlinePen1 = new Pen( Color.FromArgb( 128, Color.White ) );
private static Pen OutlinePen2 = new Pen( Color.FromArgb( 192, Color.Black ) );
/// <summary> Pen with 1x1 checker used to draw selected histogram bar. </summary>
private static Pen CheckerPen1x1 = new Pen( CheckerBWBrush1x1 );
/// <summary> Pen with 2x2 checker used to draw selected histogram bar. </summary>
private static Pen CheckerPen2x2 = new Pen( CheckerBWBrush2x2 );
/// <summary> Pen with 4x4 checker used to draw selected histogram bar. </summary>
private static Pen CheckerPen4x4 = new Pen( CheckerBWBrush4x4 );
private static Pen SelectedSolidPen = new Pen( new SolidBrush( Color.LightSteelBlue ) );
/// <summary> Pen used to draw the selected histogram bar. Size of the checker is based on the selection height. </summary>
private static Pen SelectedBarPen( FHistogramBar HistogramBar )
{
int SelectionHeight = ( int )HistogramBar.Rect.Height;
if( SelectionHeight < 8 )
{
return CheckerPen1x1;
}
else if( SelectionHeight < 16 )
{
return CheckerPen2x2;
}
return CheckerPen4x4;
}
/// <summary> Reference to the main memory profiler window. </summary>
private static MainWindow OwnerWindow;
public static void SetProfilerWindow( MainWindow InMainWindow )
{
BlackDashedPen.DashStyle = DashStyle.Dash;
RedDashedPen.DashStyle = DashStyle.Dash;
OwnerWindow = InMainWindow;
}
public static void ParseSnapshot( List<FCallStackAllocationInfo> CallStackList, string FilterText )
{
// Progress bar
long ProgressInterval = CallStackList.Count / 20;
long NextProgressUpdate = ProgressInterval;
int CallStackCurrent = 0;
OwnerWindow.ToolStripProgressBar.Value = 0;
OwnerWindow.ToolStripProgressBar.Visible = true;
OwnerWindow.UpdateStatus( "Updating histogram view for " + OwnerWindow.CurrentFilename );
List<ClassGroup> CallStackGroups = OwnerWindow.Options.ClassGroups;
List<FHistogramBar>[] Bars = new List<FHistogramBar>[ NUM_MEMORY_BANKS ];
for( int BankIndex = 0; BankIndex < Bars.Length; BankIndex++ )
{
Bars[ BankIndex ] = new List<FHistogramBar>();
// The first bar in each column is for callstacks unmatched by any pattern.
Bars[ BankIndex ].Add( new FHistogramBar( "Other", Color.White ) );
// Add all groups to all memory bank columns.
foreach( ClassGroup CallStackGroup in CallStackGroups )
{
var Bar = new FHistogramBar( CallStackGroup );
Bar.BeginBatchAddition();
Bars[ BankIndex ].Add( Bar );
}
}
using( FScopedLogTimer ParseTiming = new FScopedLogTimer( "HistogramParser.ParseSnapshot" ) )
{
long Size = 0;
int Count = 0;
bool bFilterIn = OwnerWindow.IsFilteringIn();
var FilteredCallstackList = new List<FCallStackAllocationInfo>(CallStackList.Count);
foreach (var AllocationInfo in CallStackList)
{
var FilteredAllocationInfo = AllocationInfo.GetAllocationInfoForTags(OwnerWindow.GetTagsFilter(), bFilterIn);
if (FilteredAllocationInfo.TotalCount != 0)
{
FilteredCallstackList.Add(FilteredAllocationInfo);
}
}
foreach ( FCallStackAllocationInfo AllocationInfo in FilteredCallstackList )
{
// Update progress bar.
if ( CallStackCurrent >= NextProgressUpdate )
{
OwnerWindow.ToolStripProgressBar.PerformStep();
NextProgressUpdate += ProgressInterval;
Debug.WriteLine( "FHistogramParser.ParseSnapshot " + OwnerWindow.ToolStripProgressBar.Value + "/20" );
}
CallStackCurrent++;
FCallStack OriginalCallStack = FStreamInfo.GlobalInstance.CallStackArray[AllocationInfo.CallStackIndex ];
if( OriginalCallStack.RunFilters( FilterText, CallStackGroups, bFilterIn, OwnerWindow.SelectedMemoryPool ) )
{
bool bFound = false;
int Column = FMemoryPoolInfo.GetMemoryPoolHistogramColumn( OriginalCallStack.MemoryPool );
if( Column == -1 )
{
// If the callstack is in multiple pools, just put it in the first bank.
// The user has already been warned about multi-pool callstacks.
Column = 0;
}
for( int GroupIndex = 0; GroupIndex < CallStackGroups.Count; GroupIndex++ )
{
foreach( CallStackPattern CallStackPatternIt in CallStackGroups[ GroupIndex ].CallStackPatterns )
{
if (CallStackPatternIt.ContainsCallStack(FStreamInfo.GlobalInstance.CallStackArray[AllocationInfo.CallStackIndex]))
{
Bars[Column][GroupIndex + 1].AddAllocation(AllocationInfo);
bFound = true;
goto HackyBreakAll;
}
}
}
HackyBreakAll:
if( !bFound )
{
// No pattern matched this callstack, so add it to the Other bar
Bars[ Column ][ 0 ].AddAllocation( AllocationInfo );
}
}
Size += AllocationInfo.TotalSize;
Count += AllocationInfo.TotalCount;
}
}
// End the update batch and allow things to sort
for( int BankIndex = 0; BankIndex < Bars.Length; BankIndex++ )
{
foreach( ClassGroup CallStackGroup in CallStackGroups )
{
foreach (var Bar in Bars[ BankIndex ])
{
Bar.EndBatchAddition();
}
}
}
OwnerWindow.ToolStripProgressBar.Visible = false;
HistogramBars = Bars;
// Select first valid histogram bar.
SelectFirstValidHistogramBar();
}
/// <summary> Selects first valid histogram bar, searches through all memory banks. </summary>
private static void SelectFirstValidHistogramBar()
{
SelectedMemoryBankIndex = 0;
SelectedHistogramBarIndex[ 0 ] = 0;
SelectedHistogramBarIndex[ 1 ] = 0;
SubselectedHistogramBar = null;
for( int MemoryBankIndex = 0; MemoryBankIndex < HistogramBars.Length; MemoryBankIndex++ )
{
for( int BarIndex = 0; BarIndex < HistogramBars[ MemoryBankIndex ].Count; BarIndex++ )
{
FHistogramBar Bar = FHistogramParser.HistogramBars[ MemoryBankIndex ][ BarIndex ];
if( Bar.AllocationCount > 0 )
{
SelectedMemoryBankIndex = MemoryBankIndex;
SelectedHistogramBarIndex[ SelectedMemoryBankIndex ] = BarIndex;
SelectHistogramBar( Bar );
return;
}
}
}
SelectedHistogramBar = null;
}
public static void ClearView()
{
if( HistogramBars != null )
{
HistogramBars[ 0 ].Clear();
HistogramBars[ 1 ].Clear();
}
HistogramSelectionBars.Clear();
SelectedMemoryBankIndex = 0;
SelectedHistogramBarIndex[ 0 ] = 0;
SelectedHistogramBarIndex[ 1 ] = 0;
SelectedHistogramBar = null;
SubselectedHistogramBar = null;
OwnerWindow.HistogramViewCallStackListView.BeginUpdate();
OwnerWindow.HistogramViewCallStackListView.Items.Clear();
OwnerWindow.HistogramViewCallStackListView.SelectedItems.Clear();
OwnerWindow.HistogramViewCallStackListView.EndUpdate();
OwnerWindow.HistogramViewNameLabel.Text = "";
OwnerWindow.HistogramViewSizeLabel.Text = "";
OwnerWindow.HistogramViewAllocationsLabel.Text = "";
OwnerWindow.MemoryBitmapAllocatedMemoryLabel.Text = "";
}
public static void PaintPanel( PaintEventArgs e )
{
int[] MemorySizes = new int[]
{
FStreamInfo.GetMemoryBankSize( 0 ),
FStreamInfo.GetMemoryBankSize( 1 )
};
string[] MemoryCaptions = new string[]
{
"MB (Local)",
"MB (Video)"
};
const int MinorTick = 2;
const int MajorTick = 10;
const int TickRight = 70;
const int GraphYBorder = 30;
const int BarLeft = TickRight + 10;
int NumColumns = 3;
int TotalBordersSize = NumColumns * BarLeft;
int GraphWidth = Math.Min( ( OwnerWindow.HistogramPanel.Width - TotalBordersSize ) / 3, 150 );
float BarWidth = GraphWidth * 0.6f;
float GraphXGap = GraphWidth * 0.4f;
int MaxMemorySize = 0;
for( int BankIndex = 0; BankIndex < NUM_MEMORY_BANKS; BankIndex++ )
{
MaxMemorySize = Math.Max( MemorySizes[ BankIndex ], MaxMemorySize );
}
float TotalGraphHeight = OwnerWindow.HistogramPanel.Height - GraphYBorder * 2;
if( HistogramBars != null )
{
for( int MemoryBankIndex = 0; MemoryBankIndex < MemorySizes.Length; MemoryBankIndex++ )
{
float GraphTop = GraphYBorder;
float GraphLeft = MemoryBankIndex * ( BarLeft + BarWidth + GraphXGap );
float YScale = TotalGraphHeight / MemorySizes[ MemoryBankIndex ];
// Draw vertical axes.
OwnerWindow.DrawYAxis( e.Graphics, Pens.Black, Color.Black, MajorTick, MinorTick, GraphLeft + TickRight, GraphTop, TotalGraphHeight, MemoryCaptions[ MemoryBankIndex ], MemorySizes[ MemoryBankIndex ] );
float BarY = GraphTop + YScale * MemorySizes[ MemoryBankIndex ];
for( int BarIndex = HistogramBars[ MemoryBankIndex ].Count - 1; BarIndex >= 0; BarIndex-- )
{
FHistogramBar Bar = HistogramBars[ MemoryBankIndex ][ BarIndex ];
if( Bar.AllocationCount == 0 )
{
continue;
}
float BarHeight = ( float )Bar.MemorySize / ( 1024 * 1024 ) * YScale;
Bar.Rect.X = GraphLeft + BarLeft;
Bar.Rect.Y = BarY - BarHeight;
Bar.Rect.Width = BarWidth;
Bar.Rect.Height = BarHeight;
e.Graphics.FillRectangle( new SolidBrush( Bar.Colour ), Bar.Rect.X, Bar.Rect.Y, Bar.Rect.Width, Bar.Rect.Height );
BarY -= BarHeight;
}
}
if( SelectedHistogramBar != null )
{
e.Graphics.FillRectangle
(
SelectedBarPen( SelectedHistogramBar ).Brush,
SelectedHistogramBar.Rect.X,
SelectedHistogramBar.Rect.Y,
SelectedHistogramBar.Rect.Width,
Math.Max( SelectedHistogramBar.Rect.Height, 1.0f )
);
float MarkerHeight = Math.Max( SelectedHistogramBar.Rect.Height, 1.0f );
float MarkerPosX = SelectedHistogramBar.Rect.Left + SelectedHistogramBar.Rect.Width + 1.0f;
float MarkerPosY = ( float )Math.Ceiling( ( double )SelectedHistogramBar.Rect.Top ) - 1;
e.Graphics.DrawLine( Pens.Black, MarkerPosX + 0, MarkerPosY, MarkerPosX + 5, MarkerPosY );
e.Graphics.DrawLine( Pens.Black, MarkerPosX + 5, MarkerPosY, MarkerPosX + 5, MarkerPosY + MarkerHeight );
e.Graphics.DrawLine( Pens.Black, MarkerPosX + 0, MarkerPosY + MarkerHeight + 1, MarkerPosX + 5, MarkerPosY + MarkerHeight + 1 );
}
TextRenderer.DrawText(
e.Graphics,
"Use key up or key down to change selected allocation",
OwnerWindow.AxisFont,
new Point((int)(BarLeft * 0.5f), (int)(OwnerWindow.HistogramPanel.Height - (GraphYBorder + OwnerWindow.AxisFont.Height) * 0.5f)),
Color.Black
);
}
/*
// Draw "Total Memory Used" line.
if( OwnerWindow.CurrentSnapshot != null && OwnerWindow.CurrentSnapshot.MetricArray.Count > 0 )
{
float YScale = TotalGraphHeight / MemorySizes[ 0 ];
float GraphBottom = GraphYBorder + YScale * MemorySizes[ 0 ];
float TotalUsedLineY = GraphBottom - YScale * ( OwnerWindow.CurrentSnapshot.MemoryAllocationStats.TotalAllocated / 1024f / 1024f );
float OverheadLineY = TotalUsedLineY + YScale * ( OwnerWindow.CurrentSnapshot.MetricArray[ ( int )ESnapshotMetricV3.MemoryProfilingOverhead ] / 1024f / 1024f );
e.Graphics.DrawLine( BlackDashedPen, TickRight, TotalUsedLineY, BarLeft + BarWidth, TotalUsedLineY );
e.Graphics.DrawLine( RedDashedPen, TickRight, OverheadLineY, BarLeft + BarWidth, OverheadLineY );
}
*/
if( SelectedHistogramBar != null && SelectedHistogramBar.MemorySize > 0 )
{
float GraphLeft = MemorySizes.Length * ( BarLeft + BarWidth + GraphXGap );
float MemorySizeMB = ( float )( ( double )SelectedHistogramBar.MemorySize / ( 1024 * 1024 ) );
float SelectedYScale = TotalGraphHeight / MemorySizeMB;
string AxisLabel = "MB";
int AxisMemorySize = ( int )MemorySizeMB;
if( AxisMemorySize < 32 )
{
// Drop down into kilobytes.
AxisMemorySize = ( int )( SelectedHistogramBar.MemorySize / 1024 );
AxisLabel = "KB";
}
if( AxisMemorySize < 32 )
{
// Drop down into bytes.
AxisMemorySize = ( int )Math.Max( SelectedHistogramBar.MemorySize, 32 );
AxisLabel = "bytes";
}
// Select a major tick that's divisible by 4 so that the minor tick divides into it without remainder.
int SelectedMajorTick = ( AxisMemorySize / 8 ) / 4 * 4;
int SelectedMinorTick = SelectedMajorTick / 4;
OwnerWindow.DrawYAxis( e.Graphics, Pens.Black, Color.Black, SelectedMajorTick, SelectedMinorTick, GraphLeft + TickRight, GraphYBorder, TotalGraphHeight, AxisLabel, AxisMemorySize );
{
// Used to batch up drawing as individual calls are slow
var FillRects = new Dictionary<Color, List<RectangleF>>();
var DrawRects = new List<RectangleF>();
float BarY = GraphYBorder + SelectedYScale * MemorySizeMB;
for (int SelBarIndex = HistogramSelectionBars.Count - 1; SelBarIndex >= 0; SelBarIndex--)
{
FHistogramBar Bar = HistogramSelectionBars[SelBarIndex];
float BarHeight = (float)((double)Bar.MemorySize / (1024 * 1024) * SelectedYScale);
Bar.Rect.X = GraphLeft + BarLeft;
Bar.Rect.Y = BarY - BarHeight;
Bar.Rect.Width = BarWidth;
Bar.Rect.Height = BarHeight;
List<RectangleF> FillRectsList;
if (FillRects.TryGetValue(Bar.Colour, out FillRectsList))
{
FillRectsList.Add(new RectangleF(Bar.Rect.X, Bar.Rect.Y, Bar.Rect.Width, Bar.Rect.Height));
}
else
{
FillRectsList = new List<RectangleF>() { new RectangleF(Bar.Rect.X, Bar.Rect.Y, Bar.Rect.Width, Bar.Rect.Height) };
FillRects.Add(Bar.Colour, FillRectsList);
}
DrawRects.Add(new RectangleF(Bar.Rect.X, Bar.Rect.Y, Bar.Rect.Width - 1, Bar.Rect.Height));
BarY -= BarHeight;
}
// Draw batched
foreach (var FillRectPair in FillRects)
{
e.Graphics.FillRectangles(new SolidBrush(FillRectPair.Key), FillRectPair.Value.ToArray());
}
e.Graphics.DrawRectangles(Pens.Black, DrawRects.ToArray());
}
if( SubselectedHistogramBar != null )
{
e.Graphics.FillRectangle
(
SelectedBarPen( SubselectedHistogramBar ).Brush,
SubselectedHistogramBar.Rect.X,
SubselectedHistogramBar.Rect.Y,
SubselectedHistogramBar.Rect.Width,
Math.Max( SubselectedHistogramBar.Rect.Height, 1.0f )
);
float MarkerHeight = Math.Max( SubselectedHistogramBar.Rect.Height, 1.0f );
float MarkerPosX = SubselectedHistogramBar.Rect.Left + SubselectedHistogramBar.Rect.Width + 1.0f;
float MarkerPosY = ( float )Math.Ceiling( ( double )SubselectedHistogramBar.Rect.Top ) - 1;
e.Graphics.DrawLine( Pens.Black, MarkerPosX + 0, MarkerPosY, MarkerPosX + 5, MarkerPosY );
e.Graphics.DrawLine( Pens.Black, MarkerPosX + 5, MarkerPosY, MarkerPosX + 5, MarkerPosY + MarkerHeight );
e.Graphics.DrawLine( Pens.Black, MarkerPosX + 0, MarkerPosY + MarkerHeight + 1, MarkerPosX + 5, MarkerPosY + MarkerHeight + 1 );;
}
}
}
static public void UnsafeMouseClick( object sender, MouseEventArgs e )
{
// Work out which bar, if any, the user clicked on.
for( int MemoryBankIndex = 0; MemoryBankIndex < HistogramBars.Length; MemoryBankIndex++ )
{
for( int BarIndex = 0; BarIndex < HistogramBars[ MemoryBankIndex ].Count; BarIndex++ )
{
FHistogramBar Bar = FHistogramParser.HistogramBars[ MemoryBankIndex ][ BarIndex ];
if( Bar.Rect.Contains( e.X, e.Y ) )
{
SelectedMemoryBankIndex = MemoryBankIndex;
SelectedHistogramBarIndex[ SelectedMemoryBankIndex ] = BarIndex;
SelectHistogramBar( Bar );
if( e.Button == MouseButtons.Right )
{
OwnerWindow.ViewHistoryContextMenu.Tag = Bar;
OwnerWindow.ViewHistoryContextMenu.Show( ( Control )sender, e.Location );
}
return;
}
}
}
// Check selection bars (the graph that appears when you select a bar in another graph).
if( SelectedHistogramBar != null )
{
for( int DetailedBarIndex = 0; DetailedBarIndex < HistogramSelectionBars.Count; DetailedBarIndex++ )
{
FHistogramBar HistogramBar = HistogramSelectionBars[ DetailedBarIndex ];
if( HistogramBar.Rect.Contains( e.X, e.Y ) )
{
SubselectHistogramBar( HistogramBar );
if( e.Button == MouseButtons.Right )
{
OwnerWindow.ViewHistoryContextMenu.Tag = HistogramBar;
OwnerWindow.ViewHistoryContextMenu.Show( ( Control )sender, e.Location );
}
return;
}
}
}
SubselectHistogramBar(null);
}
static public bool ProcessKeys( Keys KeyData )
{
switch( KeyData )
{
case Keys.Up:
{
if( SubselectedHistogramBar != null )
{
int Index = HistogramSelectionBars.IndexOf( SubselectedHistogramBar );
if( Index > 0 )
{
SubselectHistogramBar( HistogramSelectionBars[ Index - 1 ] );
}
}
else if( SelectedHistogramBar != null )
{
for( int BarIndex = 0; BarIndex < HistogramBars.Length; BarIndex++ )
{
List<FHistogramBar> HistogramBarArray = HistogramBars[ BarIndex ];
int Index = HistogramBarArray.IndexOf( SelectedHistogramBar );
if( Index > 0 )
{
for( ; Index > 0; Index -- )
{
if( HistogramBarArray[ Index - 1 ].AllocationCount > 0 )
{
SelectedHistogramBarIndex[ SelectedMemoryBankIndex ] = Index - 1;
SelectHistogramBar( HistogramBarArray[ Index - 1 ] );
break;
}
}
break;
}
}
}
return true;
}
case Keys.Down:
{
if( SubselectedHistogramBar != null )
{
int Index = HistogramSelectionBars.IndexOf( SubselectedHistogramBar );
if( Index != -1 && Index < HistogramSelectionBars.Count - 1 )
{
SubselectHistogramBar( HistogramSelectionBars[ Index + 1 ] );
}
}
else if( SelectedHistogramBar != null )
{
for( int BarIndex = 0; BarIndex < HistogramBars.Length; BarIndex++ )
{
List<FHistogramBar> HistogramBarArray = HistogramBars[ BarIndex ];
int Index = HistogramBarArray.IndexOf( SelectedHistogramBar );
if( Index != -1 && Index < HistogramBarArray.Count - 1 )
{
for( ; Index < HistogramBarArray.Count - 2; Index++ )
{
if( HistogramBarArray[ Index + 1 ].AllocationCount > 0 )
{
SelectedHistogramBarIndex[ SelectedMemoryBankIndex ] = Index + 1;
SelectHistogramBar( HistogramBarArray[ Index + 1 ] );
break;
}
}
break;
}
}
}
return true;
}
}
// Not processed
return false;
}
public static void SelectHistogramBar( FHistogramBar Bar )
{
if( Bar != SelectedHistogramBar )
{
// Cancel any subselection if the main selection is changing.
SubselectedHistogramBar = null;
}
SelectedHistogramBar = Bar;
if( SelectedHistogramBar != null )
{
HistogramSelectionBars.Clear();
foreach( FCallStackAllocationInfo AllocationInfo in SelectedHistogramBar.CallStackList )
{
int Address = FStreamInfo.GlobalInstance.CallStackArray[ AllocationInfo.CallStackIndex ].AddressIndices[ 0 ];
string FunctionName = FStreamInfo.GlobalInstance.NameArray[ FStreamInfo.GlobalInstance.CallStackAddressArray[ Address ].FunctionIndex ];
FHistogramBar AllocBar = new FHistogramBar( FunctionName, SelectedHistogramBar.Colour );
AllocBar.AddAllocation( AllocationInfo );
HistogramSelectionBars.Add( AllocBar );
}
}
UpdateHistogramDetails();
OwnerWindow.HistogramPanel.Invalidate();
}
public static void SubselectHistogramBar( FHistogramBar HistogramBar )
{
SubselectedHistogramBar = HistogramBar;
UpdateHistogramDetails();
OwnerWindow.HistogramPanel.Invalidate();
}
private static void UpdateHistogramDetails()
{
OwnerWindow.HistogramViewCallStackListView.BeginUpdate();
OwnerWindow.HistogramViewCallStackListView.Items.Clear();
FHistogramBar Bar = null;
if( SubselectedHistogramBar != null )
{
Bar = SubselectedHistogramBar;
}
else if( SelectedHistogramBar != null )
{
Bar = SelectedHistogramBar;
}
if( Bar != null )
{
if( Bar != SubselectedHistogramBar )
{
OwnerWindow.HistogramViewNameLabel.Text = Bar.Description;
}
OwnerWindow.HistogramViewSizeLabel.Text = MainWindow.FormatSizeString2( Bar.MemorySize );
OwnerWindow.HistogramViewAllocationsLabel.Text = Bar.AllocationCount.ToString("N0");
if( Bar.CallStackList.Count == 1 )
{
foreach( int AddressIndex in FStreamInfo.GlobalInstance.CallStackArray[ Bar.CallStackList[ 0 ].CallStackIndex ].AddressIndices )
{
string FunctionName = FStreamInfo.GlobalInstance.NameArray[ FStreamInfo.GlobalInstance.CallStackAddressArray[ AddressIndex ].FunctionIndex ];
OwnerWindow.HistogramViewCallStackListView.Items.Add( FunctionName );
}
}
}
OwnerWindow.HistogramViewCallStackListView.EndUpdate();
}
}
/// <summary> Encapsulates histogram bar information. </summary>
public class FHistogramBar
{
/// <summary> List of callstack allocations, sorted by size. </summary>
public List<FCallStackAllocationInfo> CallStackList = new List<FCallStackAllocationInfo>();
/// <summary> Memory allocated in this bar. </summary>
public long MemorySize;
/// <summary> Number of allocation in this bar. </summary>
public int AllocationCount;
/// <summary> The class group that this bar is associated with. </summary>
public ClassGroup CallStackGroup;
/// <summary> Description of this bar, usually taken from the callstack group. </summary>
public string Description;
/// <summary> The colour used to draw this bar, usually taken from the callstack group. </summary>
public Color Colour;
/// <summary> Rectangle used to draw this bar. </summary>
public RectangleF Rect;
/// <summary> > 0 if we are batch adding. See BeginBatchAddition and EndBatchAddition. </summary>
int BatchAddingCount = 0;
/// <summary> Default constructor. </summary>
public FHistogramBar( ClassGroup InCallStackGroup )
{
CallStackGroup = InCallStackGroup;
Description = InCallStackGroup.Name;
Colour = InCallStackGroup.Color;
}
/// <summary> Custom constructor. </summary>
public FHistogramBar( string InDescription, Color InColour )
{
Description = InDescription;
Colour = InColour;
}
/// <summary> Begin the process of adding a batch of new entries to this bar. Calls to AddAllocation will defer the Sort until EndBatchAddition is called. </summary>
public void BeginBatchAddition()
{
++BatchAddingCount;
}
/// <summary> End the process of adding a batch of new entries to this bar. Calls Sort to ensure new entries are in the correct order. </summary>
public void EndBatchAddition()
{
if (--BatchAddingCount < 0)
{
BatchAddingCount = 0;
}
if (BatchAddingCount == 0)
{
// Sorting largest -> smallest
CallStackList.Sort((First, Second) => Math.Sign(First.TotalSize - Second.TotalSize));
}
}
/// <summary> Inserts the new allocation so that the list stays in size order. </summary>
public void AddAllocation( FCallStackAllocationInfo AllocationInfo )
{
bool bInserted = false;
if (BatchAddingCount == 0)
{
for (int Index = 0; Index < CallStackList.Count; Index++)
{
if (CallStackList[Index].TotalSize > AllocationInfo.TotalSize)
{
CallStackList.Insert(Index, AllocationInfo);
bInserted = true;
break;
}
}
}
if (!bInserted)
{
CallStackList.Add(AllocationInfo);
}
MemorySize += AllocationInfo.TotalSize;
AllocationCount += AllocationInfo.TotalCount;
}
}
}