Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A new feature "Custom counters" #153

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ Publish/
/Samples/UnrealEnginePlugin/Binaries
/.vs
/samples/UnrealEnginePlugin/GUI/Optick.exe
/**/.idea
18 changes: 16 additions & 2 deletions gui/Optick/Controls/FrameCapture.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
<ControlsViewModel:FunctionInstanceViewModel x:Key="FunctionInstanceVM" Origin="IndividualCalls" Title="Per Call Statistics" />
<ControlsViewModel:AddressBarViewModel x:Key="AddressBarVM" />
<ControlsViewModel:CaptureSettingsViewModel x:Key="CaptureSettingsVM" />
<ControlsViewModel:PlotPanelsSettingsViewModel x:Key="CountersSettingsVM" />
<BaseControlsViewModel:SamplingViewModel x:Key="FunctionSamplingVM" Reason="AutoSample" />
<BaseControlsViewModel:SamplingViewModel x:Key="SysCallsSamplingVM" Reason="SysCall" />
</UserControl.Resources>
Expand All @@ -36,7 +37,7 @@
<CommandBinding Command="ApplicationCommands.Print" Executed="OnShowDebugInfoCommandExecuted" />
</UserControl.CommandBindings>
<UserControl.InputBindings>
<KeyBinding Modifiers="Ctrl" Key="M" Command="{Binding ShowDebugCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" />
<KeyBinding Modifiers="Ctrl" Key="M" Command="{Binding ShowDebugCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" />
</UserControl.InputBindings>
<Grid>
<Grid.RowDefinitions>
Expand Down Expand Up @@ -83,6 +84,9 @@
<ToggleButton x:Name="SettingsButton" HorizontalAlignment="Left" Width="{StaticResource DefaultButtonSize}" Height="{StaticResource DefaultButtonSize}" VerticalAlignment="Center" ToolTip="Open Capture Settings" Margin="1,0,0,0" Padding="-4">
<ContentControl Style="{StaticResource appbar_settings}" />
</ToggleButton>
<ToggleButton x:Name="CountersButton" HorizontalAlignment="Left" VerticalAlignment="Center" ToolTip="Open Counters Settings" Width="{StaticResource DefaultButtonSize}" Height="{StaticResource DefaultButtonSize}" Padding="0">
<ContentControl Style="{StaticResource appbar_counters}" />
</ToggleButton>
<Button x:Name="OpenButton" Command="ApplicationCommands.Open" HorizontalAlignment="Left" Width="{StaticResource DefaultButtonSize}" Height="{StaticResource DefaultButtonSize}" VerticalAlignment="Center" ToolTip="Open Saved Session" Margin="1,0,0,0" Padding="-6">
<ContentControl Style="{StaticResource appbar_folder_open}" />
</Button>
Expand Down Expand Up @@ -126,7 +130,7 @@
</DataTemplate>
</xcad:DockingManager.DocumentHeaderTemplate>
<xcad:LayoutRoot x:Name="_layoutRoot">
<xcad:LayoutPanel Orientation="Vertical">
<xcad:LayoutPanel Orientation="Vertical" x:Name="mainPanel">
<xcad:LayoutAnchorablePaneGroup DockHeight="2*">
<xcad:LayoutAnchorablePane x:Name="LayoutDocumentPane">
<xcad:LayoutAnchorable ContentId="document1" Title="Threads" >
Expand Down Expand Up @@ -175,6 +179,10 @@
</xcad:LayoutAnchorablePane>
</xcad:LayoutAnchorablePaneGroup>
</xcad:LayoutPanel>
<xcad:LayoutAnchorablePaneGroup DockHeight="2*" x:Name="PlotsPaneGroup">
<xcad:LayoutAnchorablePane x:Name="PlotsPane">
</xcad:LayoutAnchorablePane>
</xcad:LayoutAnchorablePaneGroup>
</xcad:LayoutPanel>
</xcad:LayoutRoot>
</xcad:DockingManager>
Expand All @@ -198,5 +206,11 @@
</Border>
</Popup>

<Popup Width="Auto" Height="Auto" Placement="Bottom" PlacementTarget="{Binding ElementName=CountersButton}" IsOpen="{Binding IsChecked, ElementName=CountersButton}" StaysOpen="False">
<Border BorderThickness="1" BorderBrush="LightGray" Background="{StaticResource OptickBackground}">
<ControlsView:PlotPanelsSettingsView DataContext="{StaticResource CountersSettingsVM}" Margin="1" PlotPanelsSettingsLoaded="OnPlotPanelsSettingsLoaded" CreatePanel="OnCreatePanel" />
</Border>
</Popup>

</Grid>
</UserControl>
164 changes: 154 additions & 10 deletions gui/Optick/Controls/FrameCapture.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
using Profiler.InfrastructureMvvm;
using Autofac;
using Profiler.Controls.ViewModels;
using Profiler.ViewModels.Plots;
using Profiler.Views;
using Xceed.Wpf.AvalonDock.Layout;
using Frame = Profiler.Data.Frame;

namespace Profiler.Controls
{
Expand All @@ -31,9 +35,10 @@ namespace Profiler.Controls
/// </summary>
public partial class FrameCapture : UserControl, INotifyPropertyChanged
{
private string _captureName;

public event PropertyChangedEventHandler PropertyChanged;
private readonly List<PlotsViewModel> _plotPanels = new List<PlotsViewModel>();
private string _captureName;

public event PropertyChangedEventHandler PropertyChanged;

protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
Expand All @@ -58,7 +63,7 @@ public SummaryViewerModel SummaryVM

public FrameCapture()
{
using (var scope = BootStrapperBase.Container.BeginLifetimeScope())
using (var scope = BootStrapperBase.Container.BeginLifetimeScope())
{
SummaryVM = scope.Resolve<SummaryViewerModel>();
}
Expand All @@ -77,13 +82,22 @@ public FrameCapture()
timeLine.NewConnection += TimeLine_NewConnection;
timeLine.CancelConnection += TimeLine_CancelConnection;
timeLine.ShowWarning += TimeLine_ShowWarning;
timeLine.DataLoaded += TimeLine_DataLoaded;
warningBlock.Visibility = Visibility.Collapsed;

AddressBarVM = (AddressBarViewModel)FindResource("AddressBarVM");
FunctionSummaryVM = (FunctionSummaryViewModel)FindResource("FunctionSummaryVM");
FunctionInstanceVM = (FunctionInstanceViewModel)FindResource("FunctionInstanceVM");
CaptureSettingsVM = (CaptureSettingsViewModel)FindResource("CaptureSettingsVM");

var plotPanelsSettingsViewModel = (PlotPanelsSettingsViewModel)FindResource("CountersSettingsVM");
plotPanelsSettingsViewModel.CustomPanels = _plotPanels;
if (string.IsNullOrEmpty(plotPanelsSettingsViewModel.CurrentPath))
plotPanelsSettingsViewModel.CurrentPath = "counters.json";

var plotPanelsSettings = PlotPanelsSettingsStorage.Load(plotPanelsSettingsViewModel.CurrentPath);
CreatePlotPanels(plotPanelsSettings);

FunctionSamplingVM = (SamplingViewModel)FindResource("FunctionSamplingVM");
SysCallsSamplingVM = (SamplingViewModel)FindResource("SysCallsSamplingVM");

Expand Down Expand Up @@ -143,17 +157,34 @@ private void TimeLine_NewConnection(object sender, RoutedEventArgs e)
AddressBarVM?.Update(args.Connection);
}

private void TimeLine_DataLoaded(object sender, RoutedEventArgs e)
{
foreach (var plotPanel in _plotPanels)
plotPanel.SetModel(timeLine.Counters);
}

private void OpenFrame(object source, FocusFrameEventArgs args)
{
Data.Frame frame = args.Frame;

if (frame is EventFrame)

if (frame is EventFrame eventFrame)
{
var group = eventFrame.Group;

EventThreadViewControl.Highlight(frame as EventFrame, null);

if (frame is EventFrame)
{
EventFrame eventFrame = frame as EventFrame;
FrameGroup group = eventFrame.Group;
if (args.FocusPlot)
{
var interval = (IDurable)frame;

var startRelative = interval.Start - frame.Group.Board.TimeSlice.Start;
var startRelativeMs = group.Board.TimeSettings.TicksToMs * startRelative;

foreach (var plotPanel in _plotPanels)
{
plotPanel.Zoom(startRelativeMs, interval.Duration);
}
}

if (eventFrame.RootEntry != null)
{
Expand Down Expand Up @@ -235,6 +266,11 @@ private void ClearButton_Click(object sender, System.Windows.RoutedEventArgs e)
SysCallInfoControl.DataContext = null;

//SamplingTreeControl.SetDescription(null, null);

foreach (var plotPanel in _plotPanels)
plotPanel.Clear();

timeLine.Counters.Clear();

MainViewModel vm = DataContext as MainViewModel;
if (vm.IsCapturing)
Expand Down Expand Up @@ -301,5 +337,113 @@ private void OnShowDebugInfoCommandExecuted(object sender, ExecutedRoutedEventAr
DebugInfoPopup.DataContext = vm;
DebugInfoPopup.IsOpen = true;
}

/// <summary>
/// Gets called when a different counters layout is loaded
/// </summary>
private void OnPlotPanelsSettingsLoaded(List<PlotPanelSerialized> plotPanelsSerialized)
{
CreatePlotPanels(plotPanelsSerialized);
}

private void CreatePlotPanels(List<PlotPanelSerialized> plotPanelsSerialized)
{
PlotsPane.Children.Clear();
_plotPanels.Clear();
foreach (var plotPanelSerialized in plotPanelsSerialized)
AddPlotPanel(plotPanelSerialized.Title, plotPanelSerialized.Counters);
}

/// <summary>
/// Gets called when "Create panel" button is being pressed
/// </summary>
private void OnCreatePanel(string title)
{
AddPlotPanel(title, null);
PlotsPane.SelectedContentIndex = PlotsPane.Children.Count - 1;
}

/// <summary>
/// Create a new panel.
/// </summary>
/// <param name="title">Panel's title</param>
/// <param name="selectedCounters">Optional, pre selected counters</param>
private void AddPlotPanel(string title, List<PlotPanelSerialized.CounterSerialized> selectedCounters)
{
var viewModel = new PlotsViewModel(title, timeLine.Counters);
var view = new PlotView
{
DataContext = viewModel
};
view.PlotClicked += (s, e) =>
{
var frame = GetFrame(timeLine.Frames, e.Time);
if (frame != null)
RaiseEvent(new FocusFrameEventArgs(GlobalEvents.FocusFrameEvent, frame, focusPlot: false));
};
if (selectedCounters != null)
{
foreach (var selectedCounter in selectedCounters)
{
var color = Color.FromArgb(selectedCounter.Color.A, selectedCounter.Color.R, selectedCounter.Color.G, selectedCounter.Color.B);
viewModel.SelectCounter(selectedCounter.Key, selectedCounter.Name, new SolidColorBrush(color), selectedCounter.DataUnits, selectedCounter.ViewUnits);
}
}

var anchorable = new LayoutAnchorable
{
CanClose = true,
Content = view,
CanHide = false
};

var titleBinding = new Binding(nameof(viewModel.Title))
{
Mode = BindingMode.OneWay,
Source = viewModel
};
BindingOperations.SetBinding(anchorable, LayoutContent.TitleProperty, titleBinding);

// Restoring layout because when an user closes a tab this elements get removed from the layout
if (PlotsPaneGroup.Parent == null)
mainPanel.Children.Add(PlotsPaneGroup);
if (PlotsPane.Parent == null)
PlotsPaneGroup.Children.Add(PlotsPane);

PlotsPane.Children.Add(anchorable);

anchorable.Closed += (s,e) =>
{
_plotPanels.Remove(viewModel);
};

_plotPanels.Add(viewModel);
}

private static Frame GetFrame(FrameCollection frameCollection, double time)
{
// binary search
var left = 0;
var right = frameCollection.Count - 1;
while (left <= right)
{
var middle = (left + right) / 2;
var frame = frameCollection[middle];

var interval = (IDurable)frame;
var startRelative = interval.Start - frame.Group.Board.TimeSlice.Start;
var startRelativeMs = frame.Group.Board.TimeSettings.TicksToMs * startRelative;

if (startRelativeMs <= time && time <= startRelativeMs + interval.Duration)
return frame;

if (startRelativeMs < time)
left = middle + 1;
else
right = middle - 1;
}

return null;
}
}
}
2 changes: 1 addition & 1 deletion gui/Optick/Controls/FunctionSearch.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ private void Flush()
if (maxFrame != null && maxEntry != null)
{
EventNode maxNode = maxFrame.Root.FindNode(maxEntry);
RaiseEvent(new FocusFrameEventArgs(GlobalEvents.FocusFrameEvent, new EventFrame(maxFrame, maxNode), null));
RaiseEvent(new FocusFrameEventArgs(GlobalEvents.FocusFrameEvent, new EventFrame(maxFrame, maxNode)));
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion gui/Optick/Controls/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ public class LocalSettings
public Platform.Connection LastConnection { get; set; }
public ThreadViewSettings ThreadSettings { get; set; } = new ThreadViewSettings();

public string TempDirectoryPath => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Optick\\Temp\\");
public string PlotPanelsSettingsFile { get; set; }

public string TempDirectoryPath => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Optick\\Temp\\");

public LocalSettings()
{
Expand Down
21 changes: 21 additions & 0 deletions gui/Optick/Converters/IsNotEmptyConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;
using System.Collections;
using System.Globalization;
using System.Windows;
using System.Windows.Data;

namespace Profiler
{
public class IsNotEmptyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((ICollection)value).Count > 0 ? Visibility.Visible : Visibility.Collapsed;
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
19 changes: 19 additions & 0 deletions gui/Optick/Converters/IsNotNullConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System;
using System.Globalization;
using System.Windows.Data;

namespace Profiler
{
public class IsNotNullConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value != null;
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
Loading