// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.Linq; using System.Xml.Linq; using PerfReportTool; using CSVStats; /* This summary 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 Example: 3,4,5,6 1,2,3,4 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 */ namespace PerfSummaries { class StatInfo { public StatInfo( StatSamples inCsvStat ) { name = inCsvStat.Name; averageValue = inCsvStat.average; csvStat = inCsvStat; } public StatInfo(string inName, double inAverageValue ) { name = inName; averageValue = inAverageValue; } public string name; public double averageValue; public StatSamples csvStat = null; public ColourThresholdList colorThresholds = null; }; class StatBudgetsSummary : Summary { public StatBudgetsSummary(XElement element, XmlVariableMappings vars, string baseXmlDirectory) { ReadStatsFromXML(element, vars); colorThresholdsTotal = ReadColourThresholdListXML(element.Element("colourThresholdsTotal"), vars); foreach (XElement child in element.Elements("colourThresholds")) { ColourThresholdList colorThresholds = ReadColourThresholdListXML(child, vars); string statName = child.GetRequiredAttribute(vars, "stat"); statColorThresholds.Add(statName, colorThresholds); } metricName = element.GetSafeAttribute(vars, "metricName"); title = element.GetSafeAttribute(vars, "title", "Budgets summary"); stripStatPrefix = element.GetSafeAttribute(vars, "stripStatPrefix"); emitStatMetrics = element.GetSafeAttribute(vars, "emitStatMetrics", false); showTotal = element.GetSafeAttribute(vars, "showTotal", true); totalStatName = element.GetSafeAttribute(vars, "totalStat"); if ( emitStatMetrics && metricName == null ) { throw new Exception("StatBudgetsSummary error: metricName must be specified if emitStatMetrics is enabled. XML element: " + element.ToString()); } } public StatBudgetsSummary() { } public override string GetName() { return "statBudgets"; } string StripStatPrefix(string statName) { if (stripStatPrefix != null) { if (statName.ToLower().StartsWith(stripStatPrefix.ToLower())) { return statName.Substring(stripStatPrefix.Length); } } return statName; } public override HtmlSection WriteSummaryData(bool bWriteHtml, CsvStats csvStats, CsvStats csvStatsUnstripped, bool bWriteSummaryCsv, SummaryTableRowData rowData, string htmlFileName) { HtmlSection htmlSection = null; // Find all referenced stats/metadata and sum them double totalValue = 0.0; List statInfoList = new List(); StatInfo totalStatInfo = null; foreach (string statName in stats) { StatSamples csvStat = csvStats.GetStat(statName); if (csvStat == null) { continue; } StatInfo statInfo = new StatInfo(csvStat); if (statColorThresholds.TryGetValue(statName, out ColourThresholdList colorThresholds)) { statInfo.colorThresholds = colorThresholds; } if (totalStatName != null && totalStatName.ToLower() == statName.ToLower()) { totalStatInfo = statInfo; totalStatInfo.name = "Total (" + StripStatPrefix(statName) + ")"; } else { statInfoList.Add(statInfo); totalValue += statInfo.averageValue; } } // Sort largest to smallest statInfoList.Sort((a, b) => b.averageValue.CompareTo(a.averageValue)); if (totalStatInfo != null) { // Add an "other" stat if the provided total stat is greater than the computed total double otherValue = totalStatInfo.averageValue - totalValue; if (otherValue > 0.0001) { StatInfo otherStatInfo = new StatInfo("Other", otherValue); totalStatInfo.colorThresholds = colorThresholdsTotal; statInfoList.Add(otherStatInfo); } } else { totalStatInfo = new StatInfo("Total", totalValue); totalStatInfo.colorThresholds = colorThresholdsTotal; } statInfoList.Add(totalStatInfo); if (rowData != null) { // If we have rowData then apply thresholds to the CsvStatAverage elements (and ensure they exist) foreach (StatInfo statInfo in statInfoList) { if ( statInfo.csvStat != null ) { // Do we have an existing CsvStatAverage metric already? SummaryTableElement element = rowData.Get(statInfo.csvStat.Name.ToLower()); if (element == null) { throw new Exception("Stat not found: "+ statInfo.csvStat.Name + " in budget summary "+title); } // Emit the stat metric if requested if (emitStatMetrics) { string shortStatName = StripStatPrefix(statInfo.name); string statMetricName = metricName + "/" + shortStatName; rowData.Add(SummaryTableElement.Type.SummaryTableMetric, statMetricName, statInfo.averageValue, statInfo.colorThresholds); } // Update the color color threshold to the CSV stat if specified if (statInfo.colorThresholds != null) { element.colorThresholdList = statInfo.colorThresholds; } } } // Output the total metric, if specified if (metricName != null) { rowData.Add(SummaryTableElement.Type.SummaryTableMetric, metricName, totalStatInfo.averageValue, totalStatInfo.colorThresholds); } } // Output HTML if (bWriteHtml) { htmlSection = new HtmlSection(title, bStartCollapsed); htmlSection.WriteLine(" "); htmlSection.WriteLine(" "); foreach (StatInfo statInfo in statInfoList) { if (showTotal == false && statInfo == totalStatInfo) { continue; } string bgcolor = "'#ffffff'"; string budgetStr = ""; if (statInfo.colorThresholds != null) { bgcolor = statInfo.colorThresholds.GetColourForValue(statInfo.averageValue); if (statInfo.colorThresholds.Thresholds.Count > 0) { budgetStr = statInfo.colorThresholds.Thresholds.First().value.ToString("0.00"); } } string displayName = statInfo.name; if (statInfo.csvStat != null) { displayName = StripStatPrefix(statInfo.name); } htmlSection.WriteLine(" "); } htmlSection.WriteLine("
StatValue (Avg)Budget
" + displayName + "" + statInfo.averageValue.ToString("0.00") + "" + budgetStr + "
"); } return htmlSection; } ColourThresholdList colorThresholdsTotal = null; Dictionary statColorThresholds = new Dictionary(); string title = ""; string metricName = null; // This is the output metric name used for the total, and also a prefix string stripStatPrefix = null; // Specifies a stat prefix to strip (applies to metric names and report HTML) string totalStatName = null; // If a total is specified then we'll use that if it's greater than the summed total. An "other" value stat will be displayed to show the difference bool emitStatMetrics = false; // Whether to emit a separate summaryTableMetric for each stat. The name of each metric will be metricName/strippedStatName bool showTotal = true; // Whether to display the total in the detailed report }; }