1076 lines
38 KiB
C++
1076 lines
38 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "Insights/ViewModels/GraphTrack.h"
|
|
|
|
#include "Framework/MultiBox/MultiBoxBuilder.h"
|
|
#include "Rendering/DrawElements.h"
|
|
#include "Styling/AppStyle.h"
|
|
#include "Widgets/Layout/SBox.h"
|
|
|
|
// TraceInsightsCore
|
|
#include "InsightsCore/Common/PaintUtils.h"
|
|
#include "InsightsCore/Common/TimeUtils.h"
|
|
|
|
// TraceInsights
|
|
#include "Insights/InsightsStyle.h"
|
|
#include "Insights/ViewModels/DrawHelpers.h"
|
|
#include "Insights/ViewModels/GraphSeries.h"
|
|
#include "Insights/ViewModels/GraphTrackBuilder.h"
|
|
#include "Insights/ViewModels/GraphTrackEvent.h"
|
|
#include "Insights/ViewModels/TimingEvent.h"
|
|
#include "Insights/ViewModels/TimingTrackViewport.h"
|
|
#include "Insights/ViewModels/TimingViewDrawHelper.h"
|
|
#include "Insights/ViewModels/TooltipDrawState.h"
|
|
#include "Insights/Widgets/SGraphSeriesList.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "GraphTrack"
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// FGraphTrack
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
INSIGHTS_IMPLEMENT_RTTI(FGraphTrack)
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
FGraphTrack::FGraphTrack()
|
|
: FBaseTimingTrack()
|
|
, WhiteBrush(FInsightsStyle::Get().GetBrush("WhiteBrush"))
|
|
, PointBrush(FInsightsStyle::GetBrush("Graph.Point"))
|
|
, BorderBrush(FInsightsStyle::Get().GetBrush("SingleBorder"))
|
|
, Font(FAppStyle::Get().GetFontStyle("SmallFont"))
|
|
{
|
|
SetValidLocations(ETimingTrackLocation::Scrollable | ETimingTrackLocation::TopDocked | ETimingTrackLocation::BottomDocked);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
FGraphTrack::FGraphTrack(const FString& InName)
|
|
: FBaseTimingTrack(InName)
|
|
, WhiteBrush(FInsightsStyle::Get().GetBrush("WhiteBrush"))
|
|
, PointBrush(FInsightsStyle::GetBrush("Graph.Point"))
|
|
, BorderBrush(FInsightsStyle::Get().GetBrush("SingleBorder"))
|
|
, Font(FAppStyle::Get().GetFontStyle("SmallFont"))
|
|
{
|
|
SetValidLocations(ETimingTrackLocation::Scrollable | ETimingTrackLocation::TopDocked | ETimingTrackLocation::BottomDocked);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
FGraphTrack::~FGraphTrack()
|
|
{
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void FGraphTrack::Reset()
|
|
{
|
|
AllSeries.Reset();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void FGraphTrack::PostUpdate(const ITimingTrackUpdateContext& Context)
|
|
{
|
|
constexpr float HeaderWidth = 100.0f;
|
|
constexpr float HeaderHeight = 14.0f;
|
|
|
|
const float MouseX = static_cast<float>(Context.GetMousePosition().X);
|
|
const float MouseY = static_cast<float>(Context.GetMousePosition().Y);
|
|
|
|
if (MouseY >= GetPosY() && MouseY < GetPosY() + GetHeight())
|
|
{
|
|
SetHoveredState(true);
|
|
SetHeaderHoveredState(MouseX < HeaderWidth && MouseY < GetPosY() + HeaderHeight);
|
|
}
|
|
else
|
|
{
|
|
SetHoveredState(false);
|
|
}
|
|
|
|
TimeScaleX = Context.GetViewport().GetScaleX();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void FGraphTrack::UpdateStats()
|
|
{
|
|
NumDrawPoints = 0;
|
|
NumDrawLines = 0;
|
|
NumDrawBoxes = 0;
|
|
|
|
for (const TSharedPtr<FGraphSeries>& Series : AllSeries)
|
|
{
|
|
if (Series->IsVisible())
|
|
{
|
|
NumDrawPoints += Series->Points.Num();
|
|
for (int32 BatchIndex = 0; BatchIndex < Series->LinePoints.Num(); ++BatchIndex)
|
|
{
|
|
NumDrawLines += Series->LinePoints[BatchIndex].Num() / 2;
|
|
}
|
|
NumDrawBoxes += Series->Boxes.Num();
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void FGraphTrack::PreDraw(const ITimingTrackDrawContext& Context) const
|
|
{
|
|
UE::Insights::FDrawContext& DrawContext = Context.GetDrawContext();
|
|
const FTimingTrackViewport& Viewport = Context.GetViewport();
|
|
|
|
FDrawHelpers::DrawBackground(DrawContext, WhiteBrush, Viewport, GetPosY(), GetHeight());
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void FGraphTrack::Draw(const ITimingTrackDrawContext& Context) const
|
|
{
|
|
UE::Insights::FDrawContext& DrawContext = Context.GetDrawContext();
|
|
const FTimingTrackViewport& Viewport = Context.GetViewport();
|
|
|
|
const float AlignedPosY = FMath::RoundToFloat(GetPosY());
|
|
const float AlignedHeight = FMath::RoundToFloat(GetHeight());
|
|
|
|
// Set clipping area.
|
|
{
|
|
const FVector2f AbsPos = FVector2f(DrawContext.Geometry.GetAbsolutePosition());
|
|
const float Scale = DrawContext.Geometry.GetAccumulatedLayoutTransform().GetScale();
|
|
const float L = AbsPos.X;
|
|
const float R = AbsPos.X + (Viewport.GetWidth() * Scale);
|
|
const float T = AbsPos.Y + (GetPosY() * Scale);
|
|
const float B = AbsPos.Y + ((GetPosY() + GetHeight()) * Scale);
|
|
const FSlateClippingZone ClipZone(FSlateRect(L, T, R, B));
|
|
DrawContext.ElementList.PushClip(ClipZone);
|
|
}
|
|
|
|
// Draw top line.
|
|
//DrawContext.DrawBox(0.0f, GetPosY(), Viewport.Width, 1.0f, WhiteBrush, FLinearColor(0.05f, 0.05f, 0.05f, 1.0f));
|
|
|
|
bool bDrawBaseline = IsAnyOptionEnabled(EGraphOptions::ShowBaseline);
|
|
for (const TSharedPtr<FGraphSeries>& Series : AllSeries)
|
|
{
|
|
if (Series->IsVisible())
|
|
{
|
|
// Draw baseline (Value == 0)...
|
|
if (bDrawBaseline)
|
|
{
|
|
bDrawBaseline = false; // only for the first visible series
|
|
|
|
const float BaselineY = FMath::RoundToFloat(static_cast<float>(Series->GetBaselineY()));
|
|
if (BaselineY >= 0.0f && BaselineY < AlignedHeight)
|
|
{
|
|
DrawContext.DrawBox(0.0f, AlignedPosY + BaselineY, Viewport.GetWidth(), 1.0f, WhiteBrush, FLinearColor(0.05f, 0.05f, 0.05f, 1.0f));
|
|
DrawContext.LayerId++;
|
|
}
|
|
}
|
|
|
|
DrawSeries(*Series, DrawContext, Viewport);
|
|
}
|
|
}
|
|
|
|
if (IsAnyOptionEnabled(EGraphOptions::ShowThresholds))
|
|
{
|
|
const FLinearColor HighThresholdLineColor(1.0f, 0.0f, 0.0f, 0.75f);
|
|
const FLinearColor LowThresholdLineColor(1.0f, 1.0f, 0.0f, 0.75f);
|
|
|
|
for (const TSharedPtr<FGraphSeries>& Series : AllSeries)
|
|
{
|
|
if (Series->IsVisible() && (Series->HasHighThresholdValue() || Series->HasLowThresholdValue()))
|
|
{
|
|
if (Series->HasHighThresholdValue())
|
|
{
|
|
const float ThresholdLineY = Series->ValueViewport.GetRoundedYForValue(Series->GetHighThresholdValue());
|
|
if (ThresholdLineY>= 0.0f && ThresholdLineY < AlignedHeight)
|
|
{
|
|
DrawContext.DrawBox(0.0f, AlignedPosY + ThresholdLineY, Viewport.GetWidth(), 1.0f, WhiteBrush, HighThresholdLineColor);
|
|
DrawContext.LayerId++;
|
|
}
|
|
}
|
|
|
|
if (Series->HasLowThresholdValue())
|
|
{
|
|
const float ThresholdLineY = Series->ValueViewport.GetRoundedYForValue(Series->GetLowThresholdValue());
|
|
if (ThresholdLineY >= 0.0f && ThresholdLineY < AlignedHeight)
|
|
{
|
|
DrawContext.DrawBox(0.0f, AlignedPosY + ThresholdLineY, Viewport.GetWidth(), 1.0f, WhiteBrush, LowThresholdLineColor);
|
|
DrawContext.LayerId++;
|
|
}
|
|
}
|
|
|
|
break; // only for the first visible series with valid thresholds
|
|
}
|
|
}
|
|
}
|
|
|
|
if (IsAnyOptionEnabled(EGraphOptions::ShowVerticalAxisGrid))
|
|
{
|
|
DrawVerticalAxisGrid(Context);
|
|
}
|
|
|
|
if (IsAnyOptionEnabled(EGraphOptions::ShowHeader))
|
|
{
|
|
DrawHeader(Context);
|
|
}
|
|
|
|
// Restore clipping.
|
|
DrawContext.ElementList.PopClip();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void FGraphTrack::DrawSeries(const FGraphSeries& Series, UE::Insights::FDrawContext& DrawContext, const FTimingTrackViewport& Viewport) const
|
|
{
|
|
const float Scale = DrawContext.Geometry.GetAccumulatedLayoutTransform().GetScale();
|
|
const float PixelUnit = 1.0f / Scale;
|
|
|
|
// Set clipping area. This area tries to take into account the optional border area that allows
|
|
// graph tracks to optionally act like 'event' tracks if required (with respect to layout, anyway).
|
|
// The GetBorderY() - 1.0f calculation is designed to avoid clipping the line rasterization, as the custom verts
|
|
// of the fill and the outer lines appear to get rasterized differently, the latter missing one pixel
|
|
// on its upper side.
|
|
{
|
|
const FVector2f AbsPos = FVector2f(DrawContext.Geometry.GetAbsolutePosition());
|
|
const float L = AbsPos.X;
|
|
const float R = AbsPos.X + (Viewport.GetWidth() * Scale);
|
|
const float T = AbsPos.Y + ((GetPosY() + (GetBorderY() - 1.0f)) * Scale);
|
|
const float B = AbsPos.Y + ((GetPosY() + (GetHeight() - (GetBorderY() - 1.0f))) * Scale);
|
|
const FSlateClippingZone ClipZone(FSlateRect(L, T, R, B));
|
|
DrawContext.ElementList.PushClip(ClipZone);
|
|
}
|
|
|
|
if (IsAnyOptionEnabled(EGraphOptions::ShowBars))
|
|
{
|
|
int32 NumBoxes = Series.Boxes.Num();
|
|
const float TrackPosY = GetPosY();
|
|
const float BaselineY = FMath::RoundToFloat(static_cast<float>(Series.GetBaselineY()));
|
|
for (int32 Index = 0; Index < NumBoxes; ++Index)
|
|
{
|
|
const FGraphSeries::FBox& Box = Series.Boxes[Index];
|
|
if (BaselineY >= Box.Y)
|
|
{
|
|
DrawContext.DrawBox(Box.X, TrackPosY + Box.Y, Box.W, BaselineY - Box.Y + 1.0f, WhiteBrush, Series.Color);
|
|
}
|
|
else
|
|
{
|
|
DrawContext.DrawBox(Box.X, TrackPosY + BaselineY, Box.W, Box.Y - BaselineY + 1.0f, WhiteBrush, Series.Color);
|
|
}
|
|
}
|
|
DrawContext.LayerId++;
|
|
}
|
|
|
|
const float LocalPosX = 0.0f;
|
|
const float LocalPosY = FMath::RoundToFloat(GetPosY());
|
|
|
|
FPaintGeometry Geo = DrawContext.Geometry.ToPaintGeometry();
|
|
Geo.AppendTransform(FSlateLayoutTransform(FVector2D(LocalPosX * Scale, LocalPosY * Scale)));
|
|
|
|
if (IsAnyOptionEnabled(EGraphOptions::ShowPolygon))
|
|
{
|
|
FSlateResourceHandle ResourceHandle = FSlateApplication::Get().GetRenderer()->GetResourceHandle(*WhiteBrush);
|
|
const FSlateShaderResourceProxy* ResourceProxy = ResourceHandle.GetResourceProxy();
|
|
|
|
FVector2D AtlasOffset = ResourceProxy ? FVector2D(ResourceProxy->StartUV) : FVector2D(0.0, 0.0);
|
|
FVector2D AtlasUVSize = ResourceProxy ? FVector2D(ResourceProxy->SizeUV) : FVector2D(1.0, 1.0);
|
|
FVector2f UV(AtlasOffset + FVector2D(0.0, 0.0) * AtlasUVSize);
|
|
|
|
const FVector2D Size = DrawContext.Geometry.GetLocalSize();
|
|
|
|
const FSlateRenderTransform& RenderTransform = Geo.GetAccumulatedRenderTransform();
|
|
|
|
FColor FillColor = Series.FillColor.ToFColor(true);
|
|
|
|
const double BaselineY = Series.GetBaselineY();
|
|
|
|
for (int32 BatchIndex = 0; BatchIndex < Series.LinePoints.Num(); ++BatchIndex)
|
|
{
|
|
const TArray<FVector2D>& LinePoints = Series.LinePoints[BatchIndex];
|
|
|
|
TArray<SlateIndex> Indices;
|
|
TArray<FSlateVertex> Verts;
|
|
|
|
Indices.Reserve(LinePoints.Num() * 6);
|
|
Verts.Reserve(LinePoints.Num() * 2);
|
|
|
|
int32 PrevSide = 0;
|
|
|
|
for (int32 PointIndex = 0; PointIndex < LinePoints.Num(); ++PointIndex)
|
|
{
|
|
const FVector2D& LinePoint = LinePoints[PointIndex];
|
|
|
|
// When crossing baseline the polygon needs to be intersected.
|
|
int32 CrtSide = (LinePoint.Y > BaselineY) ? 1 : (LinePoint.Y < BaselineY) ? -1 : 0;
|
|
if (PrevSide != 0 && CrtSide + PrevSide == 0) // alternating sides?
|
|
{
|
|
// Compute intersection point.
|
|
const FVector2D& PrevLinePoint = LinePoints[PointIndex - 1];
|
|
const double Delta = (PrevLinePoint.Y - BaselineY) / (PrevLinePoint.Y - LinePoint.Y);
|
|
const double X = PrevLinePoint.X + (LinePoint.X - PrevLinePoint.X) * Delta;
|
|
|
|
// Add an intersection point vertex.
|
|
Verts.Add(FSlateVertex::Make<ESlateVertexRounding::Disabled>(RenderTransform,
|
|
FVector2f((float)X, (float)BaselineY), UV, FillColor));
|
|
|
|
// Add a value point vertex.
|
|
Verts.Add(FSlateVertex::Make<ESlateVertexRounding::Disabled>(RenderTransform,
|
|
FVector2f((float)LinePoint.X, (float)LinePoint.Y), UV, FillColor));
|
|
|
|
// Add a baseline vertex.
|
|
Verts.Add(FSlateVertex::Make<ESlateVertexRounding::Disabled>(RenderTransform,
|
|
FVector2f((float)LinePoint.X, (float)BaselineY), UV, FillColor));
|
|
|
|
SlateIndex NumVerts = (SlateIndex)Verts.Num();
|
|
check(NumVerts >= 5);
|
|
|
|
Indices.Add(NumVerts - 5);
|
|
Indices.Add(NumVerts - 4);
|
|
Indices.Add(NumVerts - 3);
|
|
|
|
Indices.Add(NumVerts - 3);
|
|
Indices.Add(NumVerts - 2);
|
|
Indices.Add(NumVerts - 1);
|
|
}
|
|
else
|
|
{
|
|
// Add a value point vertex.
|
|
Verts.Add(FSlateVertex::Make<ESlateVertexRounding::Disabled>(RenderTransform,
|
|
FVector2f((float)LinePoint.X, (float)LinePoint.Y), UV, FillColor));
|
|
|
|
// Add a baseline vertex.
|
|
Verts.Add(FSlateVertex::Make<ESlateVertexRounding::Disabled>(RenderTransform,
|
|
FVector2f((float)LinePoint.X, (float)BaselineY), UV, FillColor));
|
|
|
|
SlateIndex NumVerts = (SlateIndex)Verts.Num();
|
|
if (NumVerts >= 4)
|
|
{
|
|
Indices.Add(NumVerts - 4);
|
|
Indices.Add(NumVerts - 3);
|
|
Indices.Add(NumVerts - 2);
|
|
|
|
Indices.Add(NumVerts - 2);
|
|
Indices.Add(NumVerts - 3);
|
|
Indices.Add(NumVerts - 1);
|
|
}
|
|
}
|
|
PrevSide = CrtSide;
|
|
}
|
|
|
|
if (Indices.Num() > 0)
|
|
{
|
|
FSlateDrawElement::MakeCustomVerts(
|
|
DrawContext.ElementList,
|
|
DrawContext.LayerId,
|
|
ResourceHandle,
|
|
Verts,
|
|
Indices,
|
|
nullptr,
|
|
0,
|
|
0, ESlateDrawEffect::None);
|
|
DrawContext.LayerId++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (IsAnyOptionEnabled(EGraphOptions::ShowLines))
|
|
{
|
|
FPaintGeometry LineGeo = Geo;
|
|
LineGeo.AppendTransform(FSlateLayoutTransform(FVector2D(0.5f, 0.5f)));
|
|
|
|
// Disable pixel snapping here so lines line up with boxes/polys correctly.
|
|
const ESlateDrawEffect LineDrawEffects = DrawContext.DrawEffects | ESlateDrawEffect::NoPixelSnapping;
|
|
|
|
#if 0
|
|
constexpr bool bAntialias = true;
|
|
const float Thickness = FMath::Max(1.0f, Scale);
|
|
#else
|
|
constexpr bool bAntialias = false;
|
|
const float Thickness = 1.0f;
|
|
#endif
|
|
|
|
for (int32 BatchIndex = 0; BatchIndex < Series.LinePoints.Num(); ++BatchIndex)
|
|
{
|
|
const TArray<FVector2D>& LinePoints = Series.LinePoints[BatchIndex];
|
|
if (LinePoints.Num() > 0)
|
|
{
|
|
FSlateDrawElement::MakeLines(DrawContext.ElementList, DrawContext.LayerId, LineGeo, LinePoints, LineDrawEffects, Series.Color, bAntialias, Thickness);
|
|
}
|
|
}
|
|
DrawContext.LayerId++;
|
|
}
|
|
|
|
// Restore clipping.
|
|
DrawContext.ElementList.PopClip();
|
|
|
|
if (IsAnyOptionEnabled(EGraphOptions::ShowPoints))
|
|
{
|
|
const int32 NumPoints = Series.Points.Num();
|
|
|
|
#define INSIGHTS_GRAPH_TRACK_DRAW_POINTS_AS_RECTANGLES 0
|
|
#if !INSIGHTS_GRAPH_TRACK_DRAW_POINTS_AS_RECTANGLES
|
|
|
|
const float OffsetX = PixelUnit / 2.0f;
|
|
const float OffsetY = PixelUnit / 2.0f;
|
|
|
|
if (IsAnyOptionEnabled(EGraphOptions::ShowPointsWithBorder))
|
|
{
|
|
// Draw points (border).
|
|
for (int32 Index = 0; Index < NumPoints; ++Index)
|
|
{
|
|
const FVector2D& Pt = Series.Points[Index];
|
|
const float PtX = LocalPosX + static_cast<float>(Pt.X) - PointVisualSize / 2.0f - 1.0f + OffsetX;
|
|
const float PtY = LocalPosY + static_cast<float>(Pt.Y) - PointVisualSize / 2.0f - 1.0f + OffsetY;
|
|
DrawContext.DrawBox(PtX, PtY, PointVisualSize + 2.0f, PointVisualSize + 2.0f, PointBrush, Series.BorderColor);
|
|
}
|
|
DrawContext.LayerId++;
|
|
}
|
|
|
|
// Draw points (interior).
|
|
for (int32 Index = 0; Index < NumPoints; ++Index)
|
|
{
|
|
const FVector2D& Pt = Series.Points[Index];
|
|
const float PtX = LocalPosX + static_cast<float>(Pt.X) - PointVisualSize / 2.0f + OffsetX;
|
|
const float PtY = LocalPosY + static_cast<float>(Pt.Y) - PointVisualSize / 2.0f + OffsetY;
|
|
DrawContext.DrawBox(PtX, PtY, PointVisualSize, PointVisualSize, PointBrush, Series.Color);
|
|
}
|
|
DrawContext.LayerId++;
|
|
|
|
#else // Alternative way of drawing points; kept here for debugging purposes.
|
|
|
|
//const float Angle = FMath::DegreesToRadians(45.0f);
|
|
|
|
if (IsAnyOptionEnabled(EGraphOptions::ShowPointsWithBorder))
|
|
{
|
|
// Draw borders.
|
|
const float BorderPtSize = PointVisualSize;
|
|
//FVector2D BorderRotationPoint(BorderPtSize / 2.0f, BorderPtSize / 2.0f);
|
|
for (int32 Index = 0; Index < NumPoints; ++Index)
|
|
{
|
|
const FVector2D& Pt = Series.Points[Index];
|
|
const float PtX = LocalPosX + static_cast<float>(Pt.X) - BorderPtSize / 2.0f + 0.5f;
|
|
const float PtY = LocalPosY + static_cast<float>(Pt.Y) - BorderPtSize / 2.0f + 0.5f;
|
|
DrawContext.DrawBox(PtX, PtY, BorderPtSize, BorderPtSize, BorderBrush, Series.BorderColor);
|
|
//DrawContext.DrawRotatedBox(PtX, PtY, BorderPtSize, BorderPtSize, BorderBrush, Series.BorderColor, Angle, BorderRotationPoint);
|
|
}
|
|
DrawContext.LayerId++;
|
|
}
|
|
|
|
// Draw points as rectangles.
|
|
const float PtSize = PointVisualSize - 2.0f;
|
|
//FVector2D RotationPoint(PtSize / 2.0f, PtSize / 2.0f);
|
|
for (int32 Index = 0; Index < NumPoints; ++Index)
|
|
{
|
|
const FVector2D& Pt = Series.Points[Index];
|
|
const float PtX = LocalPosX + static_cast<float>(Pt.X) - PtSize / 2.0f + 0.5f;
|
|
const float PtY = LocalPosY + static_cast<float>(Pt.Y) - PtSize / 2.0f + 0.5f;
|
|
DrawContext.DrawBox(PtX, PtY, PtSize, PtSize, WhiteBrush, Series.Color);
|
|
//DrawContext.DrawRotatedBox(PtX, PtY, PtSize, PtSize, WhiteBrush, Series.Color, Angle, RotationPoint);
|
|
}
|
|
DrawContext.LayerId++;
|
|
|
|
#endif
|
|
}
|
|
|
|
if (IsAnyOptionEnabled(EGraphOptions::ShowDebugInfo)) // for debugging only
|
|
{
|
|
FPaintGeometry LineGeo = Geo;
|
|
LineGeo.AppendTransform(FSlateLayoutTransform(FVector2D(0.5f, 0.5f)));
|
|
|
|
// Disable pixel snapping here so lines line up with boxes/polys correctly.
|
|
const ESlateDrawEffect LineDrawEffects = DrawContext.DrawEffects | ESlateDrawEffect::NoPixelSnapping;
|
|
|
|
if (false)
|
|
{
|
|
// Draw white corner at (0, 0) using MakeLines.
|
|
{
|
|
TArray<FVector2f> HLine;
|
|
HLine.Add(FVector2f(0.0f, 0.0f));
|
|
HLine.Add(FVector2f(10.0f, 0.0f));
|
|
FSlateDrawElement::MakeLines(DrawContext.ElementList, DrawContext.LayerId, LineGeo, HLine, LineDrawEffects, FLinearColor::White, false, 1.0f);
|
|
|
|
TArray<FVector2f> VLine;
|
|
VLine.Add(FVector2f(0.0f, 0.0f));
|
|
VLine.Add(FVector2f(0.0f, 10.0f));
|
|
FSlateDrawElement::MakeLines(DrawContext.ElementList, DrawContext.LayerId, LineGeo, VLine, LineDrawEffects, FLinearColor::White, false, 1.0f);
|
|
}
|
|
DrawContext.LayerId++;
|
|
|
|
// Draw corner at (0, 0) using DrawBox.
|
|
DrawContext.DrawBox(LocalPosX + 2.0f, LocalPosY, 6.0f, PixelUnit, WhiteBrush, Series.Color);
|
|
DrawContext.DrawBox(LocalPosX, LocalPosY + 2.0f, PixelUnit, 6.0f, WhiteBrush, Series.Color);
|
|
DrawContext.LayerId++;
|
|
}
|
|
|
|
const int32 NumDbgPoints = Series.Points.Num();
|
|
|
|
float Thickness = 1.0f;
|
|
|
|
// Draw white cross lines (17x17 screen pixels) at each point.
|
|
for (int32 Index = 0; Index < NumDbgPoints; ++Index)
|
|
{
|
|
const FVector2D& Pt = Series.Points[Index];
|
|
const float PtX = static_cast<float>(Pt.X);
|
|
const float PtY = static_cast<float>(Pt.Y);
|
|
|
|
TArray<FVector2f> HLine;
|
|
HLine.Add(FVector2f(PtX - 8.0f * PixelUnit, PtY));
|
|
HLine.Add(FVector2f(PtX + 9.0f * PixelUnit, PtY));
|
|
FSlateDrawElement::MakeLines(DrawContext.ElementList, DrawContext.LayerId, LineGeo, HLine, LineDrawEffects, FLinearColor::White, false, Thickness);
|
|
|
|
TArray<FVector2f> VLine;
|
|
VLine.Add(FVector2f(PtX, PtY - 8.0f * PixelUnit));
|
|
VLine.Add(FVector2f(PtX, PtY + 9.0f * PixelUnit));
|
|
FSlateDrawElement::MakeLines(DrawContext.ElementList, DrawContext.LayerId, LineGeo, VLine, LineDrawEffects, FLinearColor::White, false, Thickness);
|
|
}
|
|
DrawContext.LayerId++;
|
|
|
|
// Draw black cross lines (11x11 screen pixels) at each point.
|
|
for (int32 Index = 0; Index < NumDbgPoints; ++Index)
|
|
{
|
|
const FVector2D& Pt = Series.Points[Index];
|
|
const float PtX = LocalPosX + static_cast<float>(Pt.X);
|
|
const float PtY = LocalPosY + static_cast<float>(Pt.Y);
|
|
DrawContext.DrawBox(PtX - 5.0f * PixelUnit, PtY, 11.0f * PixelUnit, PixelUnit, WhiteBrush, FLinearColor::Black);
|
|
DrawContext.DrawBox(PtX, PtY - 5.0f * PixelUnit, PixelUnit, 11.0f * PixelUnit, WhiteBrush, FLinearColor::Black);
|
|
}
|
|
DrawContext.LayerId++;
|
|
|
|
// Draw a red screen pixel at each point.
|
|
for (int32 Index = 0; Index < NumDbgPoints; ++Index)
|
|
{
|
|
const FVector2D& Pt = Series.Points[Index];
|
|
const float PtX = LocalPosX + static_cast<float>(Pt.X);
|
|
const float PtY = LocalPosY + static_cast<float>(Pt.Y);
|
|
DrawContext.DrawBox(PtX, PtY, PixelUnit, PixelUnit, WhiteBrush, FLinearColor::Red);
|
|
}
|
|
DrawContext.LayerId++;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void FGraphTrack::DrawEvent(const ITimingTrackDrawContext& Context, const ITimingEvent& InTimingEvent, EDrawEventMode InDrawMode) const
|
|
{
|
|
ensure(InTimingEvent.CheckTrack(this));
|
|
ensure(InTimingEvent.Is<FGraphTrackEvent>());
|
|
|
|
const FGraphTrackEvent& GraphEvent = InTimingEvent.As<FGraphTrackEvent>();
|
|
const TSharedPtr<const FGraphSeries> Series = GraphEvent.GetSeries();
|
|
|
|
const FTimingTrackViewport& Viewport = Context.GetViewport();
|
|
UE::Insights::FDrawContext& DrawContext = Context.GetDrawContext();
|
|
|
|
const float EventX1 = Viewport.TimeToSlateUnitsRounded(GraphEvent.GetStartTime());
|
|
const float EventX2 = Viewport.TimeToSlateUnitsRounded(Viewport.RestrictEndTime(GraphEvent.GetEndTime()));
|
|
|
|
const float EventY1 = GetPosY() + Series->GetRoundedYForValue(GraphEvent.GetValue());
|
|
|
|
const FLinearColor HighlightColor(1.0f, 1.0f, 0.0f, 1.0f);
|
|
|
|
// Draw highlighted box.
|
|
if (IsAnyOptionEnabled(EGraphOptions::ShowBars))
|
|
{
|
|
float W = EventX2 - EventX1;
|
|
ensure(W >= 0); // we expect events to be sorted
|
|
|
|
// Timing events are displayed with minimum 1px (including empty ones).
|
|
if (W == 0)
|
|
{
|
|
W = 1.0f;
|
|
}
|
|
|
|
const float EventY2 = GetPosY() + static_cast<float>(Series->GetBaselineY());
|
|
|
|
const float Y1 = FMath::Min(EventY1, EventY2);
|
|
const float DY = FMath::Abs(EventY2 - EventY1);
|
|
|
|
DrawContext.DrawBox(EventX1 - 1.0f, Y1 - 1.0f, W + 2.0f, DY + 3.0f, WhiteBrush, HighlightColor);
|
|
DrawContext.LayerId++;
|
|
DrawContext.DrawBox(EventX1, Y1, W, DY + 1.0f, WhiteBrush, Series->Color);
|
|
DrawContext.LayerId++;
|
|
}
|
|
|
|
const float PX = EventX1;
|
|
const float PY = EventY1 - 0.5f;
|
|
|
|
// Draw highlighted line.
|
|
if ((EventX2 > EventX1) &&
|
|
(AreAllOptionsEnabled(EGraphOptions::UseEventDuration | EGraphOptions::ShowLines) ||
|
|
AreAllOptionsEnabled(EGraphOptions::UseEventDuration | EGraphOptions::ShowPolygon) ||
|
|
IsAnyOptionEnabled(EGraphOptions::ShowBars)))
|
|
{
|
|
float W = EventX2 - EventX1;
|
|
ensure(W >= 0); // we expect events to be sorted
|
|
|
|
// Timing events are displayed with minimum 1px (including empty ones).
|
|
if (W == 0)
|
|
{
|
|
W = 1.0f;
|
|
}
|
|
|
|
DrawContext.DrawBox(PX - 1.0f, PY - 1.0f, W + 2.0f, 3.0f, WhiteBrush, HighlightColor);
|
|
DrawContext.LayerId++;
|
|
DrawContext.DrawBox(PX, PY, W, 1.0f, WhiteBrush, Series->Color);
|
|
DrawContext.LayerId++;
|
|
}
|
|
|
|
// Draw highlighted point.
|
|
DrawContext.DrawBox(PX - PointVisualSize / 2.0f - 1.5f, PY - PointVisualSize / 2.0f - 1.5f, PointVisualSize + 4.0f, PointVisualSize + 4.0f, PointBrush, HighlightColor);
|
|
DrawContext.LayerId++;
|
|
DrawContext.DrawBox(PX - PointVisualSize / 2.0f + 0.5f, PY - PointVisualSize / 2.0f + 0.5f, PointVisualSize, PointVisualSize, PointBrush, Series->Color);
|
|
DrawContext.LayerId++;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void FGraphTrack::DrawVerticalAxisGrid(const ITimingTrackDrawContext& Context) const
|
|
{
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void FGraphTrack::DrawHeader(const ITimingTrackDrawContext& Context) const
|
|
{
|
|
const FTimingViewDrawHelper& Helper = *static_cast<const FTimingViewDrawHelper*>(&Context.GetHelper());
|
|
UE::Insights::FDrawContext& DrawContext = Context.GetDrawContext();
|
|
Helper.DrawTrackHeader(*this, DrawContext.LayerId, DrawContext.LayerId + 1);
|
|
DrawContext.LayerId += 2;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void FGraphTrack::InitTooltip(FTooltipDrawState& InOutTooltip, const ITimingEvent& InTooltipEvent) const
|
|
{
|
|
if (InTooltipEvent.CheckTrack(this) && InTooltipEvent.Is<FGraphTrackEvent>())
|
|
{
|
|
const FGraphTrackEvent& TooltipEvent = InTooltipEvent.As<FGraphTrackEvent>();
|
|
const TSharedRef<const FGraphSeries> Series = TooltipEvent.GetSeries();
|
|
|
|
InOutTooltip.ResetContent();
|
|
InOutTooltip.AddTitle(Series->GetName().ToString(), Series->GetColor());
|
|
|
|
using namespace UE::Insights;
|
|
const double Precision = FMath::Max(1.0 / TimeScaleX, FTimeValue::Nanosecond);
|
|
InOutTooltip.AddNameValueTextLine(TEXT("Time:"), FormatTime(TooltipEvent.GetStartTime(), Precision));
|
|
if (Series->HasEventDuration())
|
|
{
|
|
InOutTooltip.AddNameValueTextLine(TEXT("Duration:"), FormatTimeAuto(TooltipEvent.GetDuration()));
|
|
}
|
|
InOutTooltip.AddNameValueTextLine(TEXT("Value:"), Series->FormatValue(TooltipEvent.GetValue()));
|
|
InOutTooltip.UpdateLayout();
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
const TSharedPtr<const ITimingEvent> FGraphTrack::GetEvent(float InPosX, float InPosY, const FTimingTrackViewport& Viewport) const
|
|
{
|
|
const float LocalPosX = InPosX;
|
|
const float LocalPosY = InPosY - GetPosY();
|
|
|
|
const bool bCheckLine = AreAllOptionsEnabled(EGraphOptions::UseEventDuration | EGraphOptions::ShowLines) ||
|
|
AreAllOptionsEnabled(EGraphOptions::UseEventDuration | EGraphOptions::ShowPolygon);
|
|
|
|
const bool bCheckBox = IsAnyOptionEnabled(EGraphOptions::ShowBars);
|
|
|
|
// Search series in reverse order.
|
|
for (int32 SeriesIndex = AllSeries.Num() - 1; SeriesIndex >= 0; --SeriesIndex)
|
|
{
|
|
const TSharedPtr<FGraphSeries>& Series = AllSeries[SeriesIndex];
|
|
if (Series->IsVisible())
|
|
{
|
|
const FGraphSeriesEvent* Event = Series->GetEvent(LocalPosX, LocalPosY, Viewport, bCheckLine, bCheckBox);
|
|
if (Event != nullptr)
|
|
{
|
|
return MakeShared<FGraphTrackEvent>(SharedThis(this), Series.ToSharedRef(), *Event);
|
|
}
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void FGraphTrack::BuildContextMenu(FMenuBuilder& MenuBuilder)
|
|
{
|
|
MenuBuilder.BeginSection("Options", LOCTEXT("ContextMenu_Section_Options", "Options"));
|
|
{
|
|
if (EnumHasAnyFlags(VisibleOptions, EGraphOptions::ShowDebugInfo)) // debug functionality
|
|
{
|
|
FUIAction Action_ShowDebugInfo
|
|
(
|
|
FExecuteAction::CreateSP(this, &FGraphTrack::ContextMenu_ToggleOption_Execute, EGraphOptions::ShowDebugInfo),
|
|
FCanExecuteAction::CreateSP(this, &FGraphTrack::ContextMenu_ToggleOption_CanExecute, EGraphOptions::ShowDebugInfo),
|
|
FIsActionChecked::CreateSP(this, &FGraphTrack::ContextMenu_ToggleOption_IsChecked, EGraphOptions::ShowDebugInfo)
|
|
);
|
|
MenuBuilder.AddMenuEntry
|
|
(
|
|
LOCTEXT("ContextMenu_ShowDebugInfo", "Show Debug Info"),
|
|
LOCTEXT("ContextMenu_ShowDebugInfo_Desc", "Shows debug info."),
|
|
FSlateIcon(),
|
|
Action_ShowDebugInfo,
|
|
NAME_None,
|
|
EUserInterfaceActionType::ToggleButton
|
|
);
|
|
}
|
|
|
|
if (EnumHasAnyFlags(VisibleOptions, EGraphOptions::ShowPoints))
|
|
{
|
|
FUIAction Action_ShowPoints
|
|
(
|
|
FExecuteAction::CreateSP(this, &FGraphTrack::ContextMenu_ToggleOption_Execute, EGraphOptions::ShowPoints),
|
|
FCanExecuteAction::CreateSP(this, &FGraphTrack::ContextMenu_ToggleOption_CanExecute, EGraphOptions::ShowPoints),
|
|
FIsActionChecked::CreateSP(this, &FGraphTrack::ContextMenu_ToggleOption_IsChecked, EGraphOptions::ShowPoints)
|
|
);
|
|
MenuBuilder.AddMenuEntry
|
|
(
|
|
LOCTEXT("ContextMenu_ShowPoints", "Show Points"),
|
|
LOCTEXT("ContextMenu_ShowPoints_Desc", "Shows points."),
|
|
FSlateIcon(),
|
|
Action_ShowPoints,
|
|
NAME_None,
|
|
EUserInterfaceActionType::ToggleButton
|
|
);
|
|
}
|
|
|
|
if (EnumHasAnyFlags(VisibleOptions, EGraphOptions::ShowPointsWithBorder))
|
|
{
|
|
FUIAction Action_ShowPointsWithBorder
|
|
(
|
|
FExecuteAction::CreateSP(this, &FGraphTrack::ContextMenu_ToggleOption_Execute, EGraphOptions::ShowPointsWithBorder),
|
|
FCanExecuteAction::CreateSP(this, &FGraphTrack::ContextMenu_ShowPointsWithBorder_CanExecute),
|
|
FIsActionChecked::CreateSP(this, &FGraphTrack::ContextMenu_ToggleOption_IsChecked, EGraphOptions::ShowPointsWithBorder)
|
|
);
|
|
MenuBuilder.AddMenuEntry
|
|
(
|
|
LOCTEXT("ContextMenu_ShowPointsWithBorder", "Show Points with Border"),
|
|
LOCTEXT("ContextMenu_ShowPointsWithBorder_Desc", "Shows border around points."),
|
|
FSlateIcon(),
|
|
Action_ShowPointsWithBorder,
|
|
NAME_None,
|
|
EUserInterfaceActionType::ToggleButton
|
|
);
|
|
}
|
|
|
|
if (EnumHasAnyFlags(VisibleOptions, EGraphOptions::ShowLines))
|
|
{
|
|
FUIAction Action_ShowLines
|
|
(
|
|
FExecuteAction::CreateSP(this, &FGraphTrack::ContextMenu_ToggleOption_Execute, EGraphOptions::ShowLines),
|
|
FCanExecuteAction::CreateSP(this, &FGraphTrack::ContextMenu_ToggleOption_CanExecute, EGraphOptions::ShowLines),
|
|
FIsActionChecked::CreateSP(this, &FGraphTrack::ContextMenu_ToggleOption_IsChecked, EGraphOptions::ShowLines)
|
|
);
|
|
MenuBuilder.AddMenuEntry
|
|
(
|
|
LOCTEXT("ContextMenu_ShowLines", "Show Connected Lines"),
|
|
LOCTEXT("ContextMenu_ShowLines_Desc", "Shows connected lines. Each event is a single point in time."),
|
|
FSlateIcon(),
|
|
Action_ShowLines,
|
|
NAME_None,
|
|
EUserInterfaceActionType::ToggleButton
|
|
);
|
|
}
|
|
|
|
if (EnumHasAnyFlags(VisibleOptions, EGraphOptions::ShowPolygon))
|
|
{
|
|
FUIAction Action_ShowPolygon
|
|
(
|
|
FExecuteAction::CreateSP(this, &FGraphTrack::ContextMenu_ToggleOption_Execute, EGraphOptions::ShowPolygon),
|
|
FCanExecuteAction::CreateSP(this, &FGraphTrack::ContextMenu_ToggleOption_CanExecute, EGraphOptions::ShowPolygon),
|
|
FIsActionChecked::CreateSP(this, &FGraphTrack::ContextMenu_ToggleOption_IsChecked, EGraphOptions::ShowPolygon)
|
|
);
|
|
MenuBuilder.AddMenuEntry
|
|
(
|
|
LOCTEXT("ContextMenu_ShowPolygon", "Show Polygon"),
|
|
LOCTEXT("ContextMenu_ShowPolygon_Desc", "Shows filled polygon under the graph series."),
|
|
FSlateIcon(),
|
|
Action_ShowPolygon,
|
|
NAME_None,
|
|
EUserInterfaceActionType::ToggleButton
|
|
);
|
|
}
|
|
|
|
if (EnumHasAnyFlags(VisibleOptions, EGraphOptions::UseEventDuration))
|
|
{
|
|
FUIAction Action_UseEventDuration
|
|
(
|
|
FExecuteAction::CreateSP(this, &FGraphTrack::ContextMenu_ToggleOption_Execute, EGraphOptions::UseEventDuration),
|
|
FCanExecuteAction::CreateSP(this, &FGraphTrack::ContextMenu_UseEventDuration_CanExecute),
|
|
FIsActionChecked::CreateSP(this, &FGraphTrack::ContextMenu_ToggleOption_IsChecked, EGraphOptions::UseEventDuration)
|
|
);
|
|
MenuBuilder.AddMenuEntry
|
|
(
|
|
LOCTEXT("ContextMenu_UseEventDuration", "Use Event Duration"),
|
|
LOCTEXT("ContextMenu_UseEventDuration_Desc", "Uses duration of timing events (for Connected Lines and Polygon)."),
|
|
FSlateIcon(),
|
|
Action_UseEventDuration,
|
|
NAME_None,
|
|
EUserInterfaceActionType::ToggleButton
|
|
);
|
|
}
|
|
|
|
if (EnumHasAnyFlags(VisibleOptions, EGraphOptions::ShowBars))
|
|
{
|
|
FUIAction Action_ShowBars
|
|
(
|
|
FExecuteAction::CreateSP(this, &FGraphTrack::ContextMenu_ToggleOption_Execute, EGraphOptions::ShowBars),
|
|
FCanExecuteAction::CreateSP(this, &FGraphTrack::ContextMenu_ToggleOption_CanExecute, EGraphOptions::ShowBars),
|
|
FIsActionChecked::CreateSP(this, &FGraphTrack::ContextMenu_ToggleOption_IsChecked, EGraphOptions::ShowBars)
|
|
);
|
|
MenuBuilder.AddMenuEntry
|
|
(
|
|
LOCTEXT("ContextMenu_ShowBars", "Show Bars"),
|
|
LOCTEXT("ContextMenu_ShowBars_Desc", "Shows bars. Width of bars corresponds to duration of timing events."),
|
|
FSlateIcon(),
|
|
Action_ShowBars,
|
|
NAME_None,
|
|
EUserInterfaceActionType::ToggleButton
|
|
);
|
|
}
|
|
|
|
if (EnumHasAnyFlags(VisibleOptions, EGraphOptions::AutoZoomIncludesBaseline))
|
|
{
|
|
FUIAction Action_AutoZoomIncludesBaseline
|
|
(
|
|
FExecuteAction::CreateSP(this, &FGraphTrack::ContextMenu_ToggleOption_Execute, EGraphOptions::AutoZoomIncludesBaseline),
|
|
FCanExecuteAction::CreateSP(this, &FGraphTrack::ContextMenu_ToggleOption_CanExecute, EGraphOptions::AutoZoomIncludesBaseline),
|
|
FIsActionChecked::CreateSP(this, &FGraphTrack::ContextMenu_ToggleOption_IsChecked, EGraphOptions::AutoZoomIncludesBaseline)
|
|
);
|
|
MenuBuilder.AddMenuEntry
|
|
(
|
|
LOCTEXT("ContextMenu_AutoZoomIncludesBaseline", "Auto Zoom Includes Baseline"),
|
|
LOCTEXT("ContextMenu_AutoZoomIncludesBaseline_Desc", "If enabled, the auto zoom will also include the baseline (value == 0)."),
|
|
FSlateIcon(),
|
|
Action_AutoZoomIncludesBaseline,
|
|
NAME_None,
|
|
EUserInterfaceActionType::ToggleButton
|
|
);
|
|
}
|
|
|
|
if (EnumHasAnyFlags(VisibleOptions, EGraphOptions::AutoZoomIncludesThresholds))
|
|
{
|
|
FUIAction Action_AutoZoomIncludesThresholds
|
|
(
|
|
FExecuteAction::CreateSP(this, &FGraphTrack::ContextMenu_ToggleOption_Execute, EGraphOptions::AutoZoomIncludesThresholds),
|
|
FCanExecuteAction::CreateSP(this, &FGraphTrack::ContextMenu_ToggleOption_CanExecute, EGraphOptions::AutoZoomIncludesThresholds),
|
|
FIsActionChecked::CreateSP(this, &FGraphTrack::ContextMenu_ToggleOption_IsChecked, EGraphOptions::AutoZoomIncludesThresholds)
|
|
);
|
|
MenuBuilder.AddMenuEntry
|
|
(
|
|
LOCTEXT("ContextMenu_AutoZoomIncludesThresholds", "Auto Zoom Includes Thresholds"),
|
|
LOCTEXT("ContextMenu_AutoZoomIncludesThresholds_Desc", "If enabled, the auto zoom will also include the high and low thresholds, if available."),
|
|
FSlateIcon(),
|
|
Action_AutoZoomIncludesThresholds,
|
|
NAME_None,
|
|
EUserInterfaceActionType::ToggleButton
|
|
);
|
|
}
|
|
}
|
|
MenuBuilder.EndSection();
|
|
|
|
MenuBuilder.BeginSection("Series", LOCTEXT("ContextMenu_Section_Series", "Series"));
|
|
{
|
|
MenuBuilder.AddWidget(
|
|
SNew(SBox)
|
|
.MinDesiredWidth(250.0f)
|
|
.MaxDesiredHeight(135.0f)
|
|
[
|
|
SNew(SGraphSeriesList, SharedThis(this))
|
|
],
|
|
FText::GetEmpty(), true);
|
|
}
|
|
MenuBuilder.EndSection();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool FGraphTrack::ContextMenu_ToggleOption_CanExecute(EGraphOptions Option)
|
|
{
|
|
return EnumHasAnyFlags(EditableOptions, Option);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void FGraphTrack::ContextMenu_ToggleOption_Execute(EGraphOptions Option)
|
|
{
|
|
ToggleOptions(Option);
|
|
SetDirtyFlag();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool FGraphTrack::ContextMenu_ToggleOption_IsChecked(EGraphOptions Option)
|
|
{
|
|
return IsAnyOptionEnabled(Option);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool FGraphTrack::ContextMenu_ShowPointsWithBorder_CanExecute()
|
|
{
|
|
return EnumHasAnyFlags(EditableOptions, EGraphOptions::ShowPointsWithBorder)
|
|
&& IsAnyOptionEnabled(EGraphOptions::ShowPoints);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool FGraphTrack::ContextMenu_UseEventDuration_CanExecute()
|
|
{
|
|
return EnumHasAnyFlags(EditableOptions, EGraphOptions::UseEventDuration)
|
|
&& IsAnyOptionEnabled(EGraphOptions::ShowLines | EGraphOptions::ShowPolygon);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// FRandomGraphTrack
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
INSIGHTS_IMPLEMENT_RTTI(FRandomGraphTrack)
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
FRandomGraphTrack::FRandomGraphTrack()
|
|
: FGraphTrack()
|
|
{
|
|
EnabledOptions = //EGraphOptions::ShowDebugInfo |
|
|
EGraphOptions::ShowPoints |
|
|
EGraphOptions::ShowPointsWithBorder |
|
|
EGraphOptions::ShowLines |
|
|
//EGraphOptions::ShowPolygon |
|
|
//EGraphOptions::UseEventDuration |
|
|
//EGraphOptions::ShowBars |
|
|
//EGraphOptions::ShowBaseline |
|
|
//EGraphOptions::ShowThresholds |
|
|
//EGraphOptions::ShowVerticalAxisGrid |
|
|
EGraphOptions::ShowHeader |
|
|
EGraphOptions::None;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void FRandomGraphTrack::AddDefaultSeries()
|
|
{
|
|
TSharedRef<FGraphSeries> Series0 = MakeShared<FGraphSeries>();
|
|
Series0->SetName(TEXT("Random Blue"));
|
|
Series0->SetDescription(TEXT("Random series; for debugging purposes"));
|
|
Series0->SetColor(FLinearColor(0.1f, 0.5f, 1.0f, 1.0f), FLinearColor(0.4f, 0.8f, 1.0f, 1.0f));
|
|
Series0->SetVisibility(true);
|
|
AllSeries.Add(Series0);
|
|
|
|
TSharedRef<FGraphSeries> Series1 = MakeShared<FGraphSeries>();
|
|
Series1->SetName(TEXT("Random Yellow"));
|
|
Series1->SetDescription(TEXT("Random series; for debugging purposes"));
|
|
Series1->SetColor(FLinearColor(0.9f, 0.9f, 0.1f, 1.0f), FLinearColor(1.0f, 1.0f, 0.4f, 1.0f));
|
|
Series1->SetVisibility(false);
|
|
AllSeries.Add(Series1);
|
|
|
|
TSharedRef<FGraphSeries> Series2 = MakeShared<FGraphSeries>();
|
|
Series2->SetName(TEXT("Random Red"));
|
|
Series2->SetDescription(TEXT("Random series; for debugging purposes"));
|
|
Series2->SetColor(FLinearColor(1.0f, 0.1f, 0.2f, 1.0f), FLinearColor(1.0f, 0.4f, 0.5f, 1.0f));
|
|
Series2->SetVisibility(true);
|
|
AllSeries.Add(Series2);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
FRandomGraphTrack::~FRandomGraphTrack()
|
|
{
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void FRandomGraphTrack::Update(const ITimingTrackUpdateContext& Context)
|
|
{
|
|
FGraphTrack::Update(Context);
|
|
|
|
if (IsDirty() || Context.GetViewport().IsHorizontalViewportDirty())
|
|
{
|
|
ClearDirtyFlag();
|
|
|
|
NumAddedEvents = 0;
|
|
|
|
const FTimingTrackViewport& Viewport = Context.GetViewport();
|
|
|
|
int32 Seed = 0;
|
|
for (TSharedPtr<FGraphSeries>& Series : AllSeries)
|
|
{
|
|
Series->SetBaselineY(GetHeight() / 2.0);
|
|
Series->SetScaleY(GetHeight());
|
|
GenerateSeries(*Series, Viewport, 1000000, Seed++);
|
|
}
|
|
|
|
UpdateStats();
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void FRandomGraphTrack::GenerateSeries(FGraphSeries& Series, const FTimingTrackViewport& Viewport, const int32 EventCount, int32 Seed)
|
|
{
|
|
//////////////////////////////////////////////////
|
|
// Generate random events.
|
|
|
|
constexpr double MinDeltaTime = 0.0000001; // 100ns
|
|
constexpr double MaxDeltaTime = 0.01; // 100ms
|
|
|
|
float MinValue = (Seed == 0) ? 0.0f : (Seed == 1) ? -0.25f : -0.5f;
|
|
float MaxValue = (Seed == 0) ? 0.5f : (Seed == 1) ? +0.25f : 0.0f;
|
|
|
|
struct FGraphEvent
|
|
{
|
|
double Time;
|
|
double Duration;
|
|
double Value;
|
|
};
|
|
|
|
TArray<FGraphEvent> Events;
|
|
Events.Reserve(EventCount);
|
|
|
|
FRandomStream RandomStream(Seed);
|
|
double NextT = 0.0;
|
|
for (int32 Index = 0; Index < EventCount; ++Index)
|
|
{
|
|
FGraphEvent Ev;
|
|
Ev.Time = NextT;
|
|
const double TimeAdvance = RandomStream.GetFraction() * (MaxDeltaTime - MinDeltaTime);
|
|
NextT += MinDeltaTime + TimeAdvance;
|
|
Ev.Duration = MinDeltaTime + RandomStream.GetFraction() * TimeAdvance;
|
|
Ev.Value = MinValue + RandomStream.GetFraction() * (MaxValue - MinValue);
|
|
Events.Add(Ev);
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
// Optimize and build draw lists.
|
|
{
|
|
FGraphTrackBuilder Builder(*this, Series, Viewport);
|
|
|
|
int32 Index = 0;
|
|
while (Index < EventCount && Events[Index].Time < Viewport.GetStartTime())
|
|
{
|
|
++Index;
|
|
}
|
|
if (Index > 0)
|
|
{
|
|
Index--; // one point outside screen (left side)
|
|
}
|
|
while (Index < EventCount)
|
|
{
|
|
const FGraphEvent& Ev = Events[Index];
|
|
Builder.AddEvent(Ev.Time, Ev.Duration, Ev.Value);
|
|
|
|
if (Ev.Time > Viewport.GetEndTime())
|
|
{
|
|
// one point outside screen (right side)
|
|
break;
|
|
}
|
|
|
|
++Index;
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#undef LOCTEXT_NAMESPACE
|