Files
UnrealEngineUWP/Engine/Source/Programs/CSVTools/PerfReportTool/Summary.cs
ben woodhouse 477d1387ed PerfReportTool 4.226.0: Add a stat budgets summary type
This summary type sums up multiple CsvStat averages and emits the total as a SummaryTableMetric named metricName (if specified).
- Budget colors can optionally be applied to individual stats and/or the total via colourThresholds and colourThresholdsTotal nodes
- If emitStatMetrics is enabled, each stat will emit a SummaryTableMetric, named metricName/strippedStatName (with the same value as the existing CsvStatAverage element if it exists)
- Budget coloring is applied to existing CsvStatAverage elements.
- The budget for a given stat is assumed to be the first element of its colourThresholds. This value is displayed in the report
- A totalStatName attribute can be specified if a total stat already exists. If so, an "other" value will be output to the detailed summary table with the unaccounted value
- If the showTotal attribute is set to false (default: true), the total won't be shown in the detailed report

Other changes:
 - Always read all CSV stat averages, regardless of whether -readAllStats is specified. -readAllStats is only now used to determine whether to show CSV stat averages in a summary table

Example:
	<summary type="statBudgets" metricName="GTGameplay" title="Gamethread - Gameplay Budgets" hideStatPrefix="Exclusive/GameThread/" emitStatMetrics="1">
		<colourThresholdsTotal>3,4,5,6</colourThresholdsTotal>
		<colourThresholds stat="Exclusive/GameThread/Buildings">1,2,3,4</colourThresholds>
		<stats>Exclusive/GameThread/Buildings,Exclusive/GameThread/Camera,Exclusive/GameThread/Curie,Exclusive/GameThread/Network*,Exclusive/GameThread/PlayerController*,Exclusive/GameThread/WorldTickMisc,Exclusive/GameThread/FlushLatentActions,Exclusive/GameThread/Tickables,Exclusive/GameThread/SyncBodies,Exclusive/GameThread/CharPhys*,Exclusive/GameThread/Character*,Exclusive/GameThread/FortPawnTickSubsystem,Exclusive/GameThread/SignificanceManager,exclusive/gamethread/vehicle*,Exclusive/GameThread/AbilityTasks,Exclusive/GameThread/Actor*,Exclusive/GameThread/HandleRPC,Exclusive/GameThread/RepNotifies,Exclusive/GameThread/TickActors,Exclusive/GameThread/Pickups,Exclusive/GameThread/ProjectileMovement,Exclusive/GameThread/TimelineComponent,Exclusive/GameThread/TimerManager,Exclusive/GameThread/FortTrainManager</stats>
	</summary>
[FYI] andrew.ladenberger, michael.short

[CL 30329557 by ben woodhouse in ue5-main branch]
2023-12-14 15:20:48 -05:00

248 lines
8.5 KiB
C#

// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using CSVStats;
using PerfReportTool;
using System.Reflection;
using System.Drawing;
namespace PerfSummaries
{
class SummaryFactory
{
public static void Init()
{
summaryNameLookup = new Dictionary<string, Type>();
Assembly assembly = typeof(SummaryFactory).GetTypeInfo().Assembly;
foreach (Type type in assembly.GetTypes())
{
if (type.IsSubclassOf(typeof(Summary)))
{
Summary instance = (Summary)Activator.CreateInstance(type);
summaryNameLookup.Add(instance.GetName(), type);
}
}
}
public static Summary Create(string summaryTypeName, XElement summaryXmlElement, XmlVariableMappings vars, string baseXmlDirectory)
{
if ( summaryNameLookup.TryGetValue(summaryTypeName, out System.Type summaryType ) )
{
object[] constructArgs = new object[3] { summaryXmlElement, vars, baseXmlDirectory };
return (Summary)Activator.CreateInstance(summaryType, constructArgs);
}
throw new Exception("Summary type " + summaryType + " not found!");
}
static Dictionary<string, System.Type> summaryNameLookup = null;
};
abstract class Summary
{
public class CaptureRange
{
public string name;
public string startEvent;
public string endEvent;
public bool includeFirstFrame;
public bool includeLastFrame;
public CaptureRange(string inName, string start, string end)
{
name = inName;
startEvent = start;
endEvent = end;
includeFirstFrame = false;
includeLastFrame = false;
}
}
public class CaptureData
{
public int startIndex;
public int endIndex;
public List<float> Frames;
public CaptureData(int start, int end, List<float> inFrames)
{
startIndex = start;
endIndex = end;
Frames = inFrames;
}
}
public Summary()
{
stats = new List<string>();
captures = new List<CaptureRange>();
StatThresholds = new Dictionary<string, ColourThresholdList>();
}
public virtual void WriteSummaryData(System.IO.StreamWriter htmlFile, CsvStats csvStats, CsvStats csvStatsUnstripped, bool bWriteSummaryCsv, SummaryTableRowData rowData, string htmlFileName)
{ }
public virtual void PostInit(ReportTypeInfo reportTypeInfo, CsvStats csvStats)
{
// Resolve wildcards and remove duplicates
stats = csvStats.GetStatNamesMatchingStringList(stats.ToArray());
}
public abstract string GetName();
public void ReadStatsFromXML(XElement element, XmlVariableMappings vars)
{
useUnstrippedCsvStats = element.GetSafeAttribute<bool>(vars, "useUnstrippedCsvStats", false);
XElement statsElement = element.Element("stats");
if (statsElement != null)
{
stats = statsElement.GetValue(vars).Split(',').ToList();
}
foreach (XElement child in element.Elements())
{
if (child.Name == "capture")
{
string captureName = child.GetRequiredAttribute<string>(vars, "name");
string captureStart = child.GetRequiredAttribute<string>(vars, "startEvent");
string captureEnd = child.GetRequiredAttribute<string>(vars, "endEvent");
bool incFirstFrame = child.GetSafeAttribute<bool>(vars, "includeFirstFrame", true);
bool incLastFrame = child.GetSafeAttribute<bool>(vars, "includeLastFrame", true);
CaptureRange newRange = new CaptureRange(captureName, captureStart, captureEnd);
newRange.includeFirstFrame = incFirstFrame;
newRange.includeLastFrame = incLastFrame;
captures.Add(newRange);
}
else if (child.Name == "colourThresholds")
{
if (child.Attribute("stat") == null)
{
continue;
}
string statName = child.GetRequiredAttribute<string>(vars, "stat");
string hitchThresholdsStr = child.GetValue(vars);
if (hitchThresholdsStr == "")
{
continue;
}
string[] hitchThresholdsStrList = hitchThresholdsStr.Split(',');
ColourThresholdList HitchThresholds = new ColourThresholdList();
for (int i = 0; i < hitchThresholdsStrList.Length; i++)
{
string hitchThresholdStr = hitchThresholdsStrList[i];
string hitchThresholdNumStr = hitchThresholdStr;
Colour thresholdColour = null;
int openBracketIndex = hitchThresholdStr.IndexOf('(');
if (openBracketIndex != -1 )
{
hitchThresholdNumStr = hitchThresholdStr.Substring(0, openBracketIndex);
int closeBracketIndex = hitchThresholdStr.IndexOf(')');
if (closeBracketIndex > openBracketIndex)
{
string colourString = hitchThresholdStr.Substring(openBracketIndex+1, closeBracketIndex - openBracketIndex-1);
thresholdColour = new Colour(colourString);
}
}
double thresholdValue = Convert.ToDouble(hitchThresholdNumStr, System.Globalization.CultureInfo.InvariantCulture);
HitchThresholds.Add(new ThresholdInfo(thresholdValue, thresholdColour));
}
if (HitchThresholds.Count == 4)
{
StatThresholds.Add(statName, HitchThresholds);
}
}
}
}
public CaptureData GetFramesForCapture(CaptureRange inCapture, List<float> FrameTimes, List<CsvEvent> EventsCaptured)
{
List<float> ReturnFrames = new List<float>();
int startFrame = -1;
int endFrame = FrameTimes.Count;
for (int i = 0; i < EventsCaptured.Count; i++)
{
if (startFrame < 0 && EventsCaptured[i].Name.ToLower().Contains(inCapture.startEvent.ToLower()))
{
startFrame = EventsCaptured[i].Frame;
if (!inCapture.includeFirstFrame)
{
startFrame++;
}
}
else if (endFrame >= FrameTimes.Count && EventsCaptured[i].Name.ToLower().Contains(inCapture.endEvent.ToLower()))
{
endFrame = EventsCaptured[i].Frame;
if (!inCapture.includeLastFrame)
{
endFrame--;
}
}
}
if (startFrame == -1 || endFrame == FrameTimes.Count || endFrame < startFrame)
{
return null;
}
ReturnFrames = FrameTimes.GetRange(startFrame, (endFrame - startFrame));
CaptureData CaptureToUse = new CaptureData(startFrame, endFrame, ReturnFrames);
return CaptureToUse;
}
public string[] GetUniqueStatNames()
{
HashSet<string> uniqueStats = new HashSet<string>();
foreach (string stat in stats)
{
if (!uniqueStats.Contains(stat))
{
uniqueStats.Add(stat);
}
}
return uniqueStats.ToArray();
}
protected ColourThresholdList ReadColourThresholdListXML(XElement colourThresholdEl, XmlVariableMappings vars)
{
return ColourThresholdList.ReadColourThresholdListXML(colourThresholdEl, vars);
}
protected double [] ReadColourThresholdsXML(XElement colourThresholdEl, XmlVariableMappings vars)
{
return ColourThresholdList.ReadColourThresholdsXML(colourThresholdEl, vars);
}
public string GetStatThresholdColour(string StatToUse, double value)
{
ColourThresholdList Thresholds = GetStatColourThresholdList(StatToUse);
if (Thresholds != null)
{
return Thresholds.GetColourForValue(value);
}
return "'#ffffff'";
}
public ColourThresholdList GetStatColourThresholdList(string StatToUse)
{
if (StatThresholds.ContainsKey(StatToUse))
{
return StatThresholds[StatToUse];
}
return null;
}
public List<CaptureRange> captures;
public List<string> stats;
public Dictionary<string, ColourThresholdList> StatThresholds;
public bool useUnstrippedCsvStats;
};
}