2018-10-02 23:31:54 +01:00
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using System.Windows;
|
|
|
|
|
using Profiler.Data;
|
|
|
|
|
using System.Windows.Media;
|
|
|
|
|
using Profiler.DirectX;
|
|
|
|
|
using System.Windows.Forms;
|
2019-04-14 18:30:30 +01:00
|
|
|
using Profiler.InfrastructureMvvm;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
|
2020-02-05 20:51:54 +00:00
|
|
|
namespace Profiler.Controls
|
2018-10-02 23:31:54 +01:00
|
|
|
{
|
|
|
|
|
public static class RenderParams
|
|
|
|
|
{
|
2018-10-18 19:25:33 +01:00
|
|
|
private static double baseHeight = 16.0;
|
2019-04-22 01:05:47 +01:00
|
|
|
public static double BaseHeight { get { return baseHeight * DirectX.RenderSettings.dpiScaleY; } }
|
2018-10-18 19:25:33 +01:00
|
|
|
private static double baseMargin = 0.75;
|
|
|
|
|
public static double BaseMargin { get { return baseMargin * DirectX.RenderSettings.dpiScaleY; } }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public struct Interval
|
|
|
|
|
{
|
|
|
|
|
public double Left;
|
|
|
|
|
public double Width;
|
|
|
|
|
|
|
|
|
|
public double Right { get { return Left + Width; } }
|
|
|
|
|
|
|
|
|
|
public void Normalize()
|
|
|
|
|
{
|
|
|
|
|
Width = Math.Max(0.0, Math.Min(Width, 1.0));
|
|
|
|
|
Left = Math.Max(0.0, Math.Min(Left, 1.0 - Width));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Interval(double left, double width)
|
|
|
|
|
{
|
|
|
|
|
Left = left;
|
|
|
|
|
Width = width;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool Intersect(Interval other)
|
|
|
|
|
{
|
|
|
|
|
return Right >= other.Left && other.Right >= Left;
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-10 01:04:19 +00:00
|
|
|
public bool Contains(Interval other)
|
|
|
|
|
{
|
|
|
|
|
return Left <= other.Left && other.Right <= Right;
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-18 19:25:33 +01:00
|
|
|
public static Interval Unit = new Interval(0.0, 1.0);
|
2018-12-10 01:04:19 +00:00
|
|
|
public static Interval Zero = new Interval(0.0, 0.0);
|
2019-12-08 20:55:55 +00:00
|
|
|
|
|
|
|
|
public bool IsValid { get { return Width > double.Epsilon; } }
|
2018-10-18 19:25:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class ThreadScroll
|
|
|
|
|
{
|
|
|
|
|
public Durable TimeSlice { get; set; }
|
|
|
|
|
public double Width { get; set; }
|
|
|
|
|
public double Height { get; set; }
|
|
|
|
|
|
2018-12-10 01:04:19 +00:00
|
|
|
public Interval ViewUnit = Interval.Zero;
|
2018-10-18 19:25:33 +01:00
|
|
|
|
|
|
|
|
public Durable ViewTime { get { return UnitToTime(ViewUnit); } }
|
|
|
|
|
|
2019-07-12 15:22:24 +01:00
|
|
|
|
|
|
|
|
const double MIN_WIDTH = 0.000001;
|
|
|
|
|
public double Zoom { get { return 1.0 / Math.Max(MIN_WIDTH, ViewUnit.Width); } }
|
2018-10-02 23:31:54 +01:00
|
|
|
|
|
|
|
|
public double TimeToUnit(ITick tick)
|
|
|
|
|
{
|
|
|
|
|
double durationTicks = TimeSlice.Finish - TimeSlice.Start;
|
|
|
|
|
return (tick.Start - TimeSlice.Start) / durationTicks;
|
2018-10-18 19:25:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Interval TimeToUnit(IDurable d)
|
|
|
|
|
{
|
|
|
|
|
double durationTicks = TimeSlice.Finish - TimeSlice.Start;
|
|
|
|
|
return new Interval((d.Start - TimeSlice.Start) / durationTicks, (d.Finish - d.Start) / durationTicks);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Interval TimeToPixel(IDurable d)
|
|
|
|
|
{
|
|
|
|
|
Interval unit = TimeToUnit(d);
|
|
|
|
|
double scale = Width * Zoom;
|
|
|
|
|
return new Interval((unit.Left - ViewUnit.Left) * scale, unit.Width * scale);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public double TimeToPixel(ITick t)
|
|
|
|
|
{
|
|
|
|
|
double unit = TimeToUnit(t);
|
|
|
|
|
double scale = Width * Zoom;
|
|
|
|
|
return (unit - ViewUnit.Left) * scale;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public double PixelToUnitLength(double pixelX)
|
|
|
|
|
{
|
|
|
|
|
return (pixelX / Width) * ViewUnit.Width;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public ITick PixelToTime(double pixelX)
|
|
|
|
|
{
|
|
|
|
|
double unit = ViewUnit.Left + PixelToUnitLength(pixelX);
|
2019-03-24 13:15:45 +00:00
|
|
|
return TimeSlice != null ? new Tick() { Start = TimeSlice.Start + (long)(unit * (TimeSlice.Finish - TimeSlice.Start)) } : new Tick();
|
2018-10-18 19:25:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Durable UnitToTime(Interval unit)
|
|
|
|
|
{
|
|
|
|
|
long duration = TimeSlice.Finish - TimeSlice.Start;
|
|
|
|
|
return new Durable(TimeSlice.Start + (long)(ViewUnit.Left * duration), TimeSlice.Start + (long)(ViewUnit.Right * duration));
|
2018-10-02 23:31:54 +01:00
|
|
|
}
|
|
|
|
|
|
2018-11-05 16:39:22 +00:00
|
|
|
public CallStackReason DrawCallstacks { get; set; }
|
2019-12-08 20:55:55 +00:00
|
|
|
public bool DrawDataTags { get; set; }
|
2018-10-18 19:25:33 +01:00
|
|
|
|
|
|
|
|
public enum SyncDrawType
|
|
|
|
|
{
|
|
|
|
|
Wait,
|
|
|
|
|
Work,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public SyncDrawType SyncDraw { get; set; }
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-14 18:30:30 +01:00
|
|
|
public abstract class ThreadRow : BaseViewModel
|
2018-10-18 19:25:33 +01:00
|
|
|
{
|
2019-04-14 18:30:30 +01:00
|
|
|
public FrameworkElement Header { get; set; }
|
|
|
|
|
|
2018-10-18 19:25:33 +01:00
|
|
|
public double Offset { get; set; }
|
|
|
|
|
public abstract double Height { get; }
|
|
|
|
|
public abstract String Name { get; }
|
|
|
|
|
public FrameGroup Group { get; set; }
|
|
|
|
|
|
|
|
|
|
public abstract void Render(DirectX.DirectXCanvas canvas, ThreadScroll scroll, DirectXCanvas.Layer layer, Rect box);
|
|
|
|
|
public abstract void BuildMesh(DirectX.DirectXCanvas canvas, ThreadScroll scroll);
|
|
|
|
|
|
|
|
|
|
public abstract void OnMouseMove(Point point, ThreadScroll scroll);
|
|
|
|
|
public abstract void OnMouseHover(Point point, ThreadScroll scroll, List<object> dataContext);
|
|
|
|
|
public abstract void OnMouseClick(Point point, ThreadScroll scroll);
|
|
|
|
|
public abstract void ApplyFilter(DirectX.DirectXCanvas canvas, ThreadScroll scroll, HashSet<EventDescription> descriptions);
|
2018-12-16 21:52:57 +00:00
|
|
|
|
2019-04-22 11:44:25 +01:00
|
|
|
public Matrix GetWorldMatrix(ThreadScroll scroll, bool useMargin = true)
|
2018-12-16 21:52:57 +00:00
|
|
|
{
|
2019-04-22 11:44:25 +01:00
|
|
|
return new Matrix(scroll.Zoom, 0.0, 0.0, (Height - (useMargin ? 2.0 * RenderParams.BaseMargin : 0.0)) / scroll.Height,
|
2018-12-16 21:52:57 +00:00
|
|
|
-(scroll.ViewUnit.Left * scroll.Zoom),
|
2019-04-22 11:44:25 +01:00
|
|
|
(Offset + (useMargin ? 1.0 * RenderParams.BaseMargin : 0.0)) / scroll.Height);
|
2018-12-16 21:52:57 +00:00
|
|
|
}
|
2019-04-14 18:30:30 +01:00
|
|
|
|
|
|
|
|
public delegate void OnVisibilityChangedHandler(ThreadRow row);
|
|
|
|
|
public event OnVisibilityChangedHandler VisibilityChanged;
|
|
|
|
|
|
|
|
|
|
private bool _isVisible = true;
|
|
|
|
|
public bool IsVisible
|
|
|
|
|
{
|
|
|
|
|
get { return _isVisible; }
|
|
|
|
|
set { SetProperty(ref _isVisible, value); VisibilityChanged?.Invoke(this); }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public delegate void OnExpanedChangedHandler(ThreadRow row);
|
|
|
|
|
public event OnExpanedChangedHandler ExpandChanged;
|
|
|
|
|
|
2019-08-18 12:25:03 +01:00
|
|
|
protected bool _isExpanded = true;
|
2019-04-14 18:30:30 +01:00
|
|
|
public bool IsExpanded
|
|
|
|
|
{
|
|
|
|
|
get { return _isExpanded; }
|
|
|
|
|
set { SetProperty(ref _isExpanded, value); ExpandChanged?.Invoke(this); }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private long _busyCounter = 0;
|
|
|
|
|
public bool IsBusy
|
|
|
|
|
{
|
|
|
|
|
get { return Interlocked.Read(ref _busyCounter) > 0; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void SetBusy(bool isBusy)
|
|
|
|
|
{
|
|
|
|
|
bool changed = false;
|
|
|
|
|
if (isBusy)
|
|
|
|
|
{
|
|
|
|
|
if (Interlocked.Increment(ref _busyCounter) == 1)
|
|
|
|
|
changed = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (Interlocked.Decrement(ref _busyCounter) == 0)
|
|
|
|
|
changed = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (changed)
|
|
|
|
|
{
|
|
|
|
|
System.Windows.Application.Current.Dispatcher.Invoke(new Action(() => OnPropertyChanged("IsBusy")));
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-10-18 19:25:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class HeaderThreadRow : ThreadRow
|
|
|
|
|
{
|
2019-03-24 13:15:45 +00:00
|
|
|
public static double DefaultHeaderHeight => RenderParams.BaseHeight * 1.25;
|
2019-04-22 01:05:47 +01:00
|
|
|
public static double DefaultHeaderHeightDPI => DefaultHeaderHeight / RenderSettings.dpiScaleY;
|
2019-03-24 13:15:45 +00:00
|
|
|
public override double Height { get { return DefaultHeaderHeight; } }
|
2018-10-18 19:25:33 +01:00
|
|
|
public override string Name { get { return String.Empty; } }
|
|
|
|
|
|
|
|
|
|
public Color GradientTop { get; set; }
|
|
|
|
|
public Color GradientBottom { get; set; }
|
|
|
|
|
public Color TextColor { get; set; }
|
2019-03-24 13:15:45 +00:00
|
|
|
public Color TickColor { get; set; } = Colors.Gray;
|
2018-10-18 19:25:33 +01:00
|
|
|
public HeaderThreadRow(FrameGroup group)
|
|
|
|
|
{
|
|
|
|
|
Group = group;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DirectX.Mesh BackgroundMeshLines { get; set; }
|
|
|
|
|
DirectX.Mesh BackgroundMeshTris { get; set; }
|
|
|
|
|
|
2019-03-24 13:15:45 +00:00
|
|
|
bool EnableTickers { get; set; }
|
|
|
|
|
|
2018-10-18 19:25:33 +01:00
|
|
|
public override void BuildMesh(DirectX.DirectXCanvas canvas, ThreadScroll scroll)
|
|
|
|
|
{
|
|
|
|
|
DirectX.DynamicMesh builder = canvas.CreateMesh();
|
|
|
|
|
builder.Geometry = DirectX.Mesh.GeometryType.Lines;
|
|
|
|
|
|
2019-04-22 11:44:25 +01:00
|
|
|
double headerHeight = 1.0;//(Height - RenderParams.BaseMargin) / scroll.Height;
|
2019-03-24 13:15:45 +00:00
|
|
|
|
|
|
|
|
// Adding Tickers
|
|
|
|
|
if (EnableTickers)
|
|
|
|
|
{
|
|
|
|
|
for (double tick = Math.Ceiling(scroll.TimeSlice.StartMS); tick < Math.Ceiling(scroll.TimeSlice.FinishMS); tick += 1.0)
|
|
|
|
|
{
|
|
|
|
|
double longX = scroll.TimeToUnit(new Tick { Start = Durable.MsToTick(tick) });
|
|
|
|
|
builder.AddLine(new Point(longX, headerHeight * 3.0 / 6.0), new Point(longX, headerHeight), TickColor);
|
|
|
|
|
|
|
|
|
|
double medX = scroll.TimeToUnit(new Tick { Start = Durable.MsToTick(tick + 0.5) });
|
|
|
|
|
builder.AddLine(new Point(medX, headerHeight * 4.0 / 6.0), new Point(medX, headerHeight), TickColor);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (double miniTick = 0.1; miniTick < 1.0; miniTick += 0.1)
|
|
|
|
|
{
|
|
|
|
|
double miniX = scroll.TimeToUnit(new Tick { Start = Durable.MsToTick(tick + miniTick) });
|
|
|
|
|
builder.AddLine(new Point(miniX, headerHeight * 5.0 / 6.0), new Point(miniX, headerHeight), TickColor);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-10-18 19:25:33 +01:00
|
|
|
BackgroundMeshLines = builder.Freeze(canvas.RenderDevice);
|
|
|
|
|
|
|
|
|
|
DirectX.DynamicMesh builderHeader = canvas.CreateMesh();
|
2019-03-24 13:15:45 +00:00
|
|
|
builderHeader.AddRect(new Rect(0.0, 0.0, 1.0, headerHeight), new Color[] { GradientTop, GradientTop, GradientBottom, GradientBottom });
|
2018-10-18 19:25:33 +01:00
|
|
|
BackgroundMeshTris = builderHeader.Freeze(canvas.RenderDevice);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void Render(DirectXCanvas canvas, ThreadScroll scroll, DirectXCanvas.Layer layer, Rect box)
|
|
|
|
|
{
|
2018-10-21 01:41:18 +01:00
|
|
|
if (layer == DirectXCanvas.Layer.Foreground)
|
2018-10-18 19:25:33 +01:00
|
|
|
{
|
2019-04-22 11:44:25 +01:00
|
|
|
Matrix world = GetWorldMatrix(scroll, false);
|
2018-10-18 19:25:33 +01:00
|
|
|
|
2019-04-22 11:44:25 +01:00
|
|
|
//Matrix world = new Matrix(scroll.Zoom, 0.0, 0.0, 1.0, -scroll.ViewUnit.Left * scroll.Zoom, 0.0);
|
2018-10-18 19:25:33 +01:00
|
|
|
|
2019-04-22 11:44:25 +01:00
|
|
|
if (BackgroundMeshTris != null)
|
|
|
|
|
{
|
|
|
|
|
BackgroundMeshTris.WorldTransform = world;
|
|
|
|
|
canvas.Draw(BackgroundMeshTris);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (BackgroundMeshLines != null)
|
|
|
|
|
{
|
|
|
|
|
BackgroundMeshLines.WorldTransform = world;
|
|
|
|
|
canvas.Draw(BackgroundMeshLines);
|
|
|
|
|
}
|
2018-10-18 19:25:33 +01:00
|
|
|
|
2019-03-24 13:15:45 +00:00
|
|
|
double yOffset = Offset + (Height - RenderParams.BaseHeight) * 0.5;
|
|
|
|
|
|
2020-01-07 00:11:39 +00:00
|
|
|
FrameList focusThread = Group.FocusThread;
|
|
|
|
|
if (focusThread != null)
|
2018-10-18 19:25:33 +01:00
|
|
|
{
|
2020-01-07 00:11:39 +00:00
|
|
|
Data.Utils.ForEachInsideInterval(focusThread.Events, scroll.ViewTime, (frame, index) =>
|
2019-12-08 20:55:55 +00:00
|
|
|
{
|
2020-01-07 00:11:39 +00:00
|
|
|
Interval interval = scroll.TimeToPixel(frame);
|
|
|
|
|
String text = String.Format(System.Globalization.CultureInfo.InvariantCulture, "Frame {0} ({1:0.0}ms)", (uint)index, frame.Duration);
|
2018-10-18 19:25:33 +01:00
|
|
|
|
2019-12-08 20:55:55 +00:00
|
|
|
// 2 times to emulate "bold"
|
|
|
|
|
for (int i = 0; i < 2; ++i)
|
|
|
|
|
canvas.Text.Draw(new Point(interval.Left, yOffset), text, TextColor, TextAlignment.Center, interval.Width);
|
|
|
|
|
});
|
|
|
|
|
}
|
2018-10-18 19:25:33 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void OnMouseMove(Point point, ThreadScroll scroll) { }
|
|
|
|
|
public override void OnMouseHover(Point point, ThreadScroll scroll, List<object> dataContext) { }
|
|
|
|
|
public override void OnMouseClick(Point point, ThreadScroll scroll) { }
|
|
|
|
|
public override void ApplyFilter(DirectXCanvas canvas, ThreadScroll scroll, HashSet<EventDescription> descriptions) { }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class EventFilter
|
|
|
|
|
{
|
|
|
|
|
public FrameGroup Group { get; set; }
|
|
|
|
|
public HashSet<EventDescription> Descriptions { get; set; }
|
|
|
|
|
|
|
|
|
|
public List<Entry> Entries = new List<Entry>();
|
|
|
|
|
|
|
|
|
|
Object criticalSection = new Object();
|
|
|
|
|
|
|
|
|
|
public delegate void LoadedEventHandler();
|
|
|
|
|
|
|
|
|
|
void Load(ThreadData data)
|
|
|
|
|
{
|
|
|
|
|
data.Events.ForEach(frame =>
|
|
|
|
|
{
|
|
|
|
|
foreach (EventDescription desc in Descriptions)
|
|
|
|
|
{
|
|
|
|
|
List<Entry> filteredEntries = frame.ShortBoard.Get(desc);
|
|
|
|
|
|
|
|
|
|
if (filteredEntries != null)
|
|
|
|
|
{
|
|
|
|
|
lock (criticalSection)
|
|
|
|
|
{
|
|
|
|
|
Entries.AddRange(filteredEntries);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
lock (criticalSection)
|
|
|
|
|
{
|
|
|
|
|
Entries.Sort();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static EventFilter Create(ThreadData data, HashSet<EventDescription> descriptions)
|
|
|
|
|
{
|
|
|
|
|
if (descriptions == null)
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
EventFilter result = new EventFilter() { Descriptions = descriptions };
|
|
|
|
|
result.Load(data);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-10-02 23:31:54 +01:00
|
|
|
}
|