Collect and show perf sample totals in widget

Both the rolling and cumulative active (not paused) means are shown.
This commit is contained in:
atlimit8
2025-05-28 04:51:47 -05:00
committed by Gustas Kažukauskas
parent ef5541cce4
commit 2516c5a798
4 changed files with 138 additions and 11 deletions

View File

@@ -661,7 +661,7 @@ namespace OpenRA
world.Tick();
PerfHistory.Tick();
PerfHistory.Tick(!world.Paused);
}
// Wait until we have done our first world Tick before TickRendering
@@ -759,12 +759,13 @@ namespace OpenRA
}
}
PerfHistory.Items["render"].Tick();
PerfHistory.Items["batches"].Tick();
PerfHistory.Items["render_world"].Tick();
PerfHistory.Items["render_widgets"].Tick();
PerfHistory.Items["render_flip"].Tick();
PerfHistory.Items["terrain_lighting"].Tick();
var isActive = !(worldRenderer?.World.Paused ?? true);
PerfHistory.Items["render"].Tick(isActive);
PerfHistory.Items["batches"].Tick(isActive);
PerfHistory.Items["render_world"].Tick(isActive);
PerfHistory.Items["render_widgets"].Tick(isActive);
PerfHistory.Items["render_flip"].Tick(isActive);
PerfHistory.Items["terrain_lighting"].Tick(isActive);
}
static void Loop()

View File

@@ -41,11 +41,11 @@ namespace OpenRA.Support
Items[item].Val += x;
}
public static void Tick()
public static void Tick(bool gameIsActive = true)
{
foreach (var item in Items.Values)
if (item.HasNormalTick)
item.Tick();
item.Tick(gameIsActive);
}
public static void Reset()

View File

@@ -22,6 +22,8 @@ namespace OpenRA.Support
public double Val = 0.0;
int head = 1, tail = 0;
public bool HasNormalTick = true;
public double ActiveGameTotal { get; private set; } = 0.0;
public int ActiveGameTotalSamples { get; private set; } = 0;
public PerfItem(string name, Color c)
{
@@ -29,9 +31,15 @@ namespace OpenRA.Support
C = c;
}
public void Tick()
public void Tick(bool gameIsActive = true)
{
samples[head++] = Val;
if (gameIsActive)
{
ActiveGameTotal += Val;
ActiveGameTotalSamples++;
}
if (head == samples.Length) head = 0;
if (head == tail && ++tail == samples.Length) tail = 0;
Val = 0.0;
@@ -63,6 +71,11 @@ namespace OpenRA.Support
return i == 0 ? sum : sum / i;
}
public double ActiveGameTotalAverage()
{
return ActiveGameTotal / ActiveGameTotalSamples;
}
public double LastValue
{
get
@@ -78,6 +91,8 @@ namespace OpenRA.Support
head = 1;
tail = 0;
Val = 0.0;
ActiveGameTotal = 0.0;
ActiveGameTotalSamples = 0;
}
}
}

View File

@@ -9,7 +9,10 @@
*/
#endregion
using System;
using System.Linq;
using System.Text;
using OpenRA.Graphics;
using OpenRA.Primitives;
using OpenRA.Support;
using OpenRA.Widgets;
@@ -18,6 +21,92 @@ namespace OpenRA.Mods.Common.Widgets
{
public class PerfGraphWidget : Widget
{
readonly int dotWidth;
readonly int nextDotAdvance;
readonly Cache<string, int> textWidthCache;
readonly SpriteFont font = Game.Renderer.Fonts["Tiny"];
StringBuilder builder = null;
public PerfGraphWidget()
{
textWidthCache = new(GetTextWidth);
dotWidth = textWidthCache["."];
nextDotAdvance = textWidthCache[".."] - dotWidth;
}
int GetTextWidth(string text) => font.Measure(text).X;
public void SixCharacterFormatFloat(StringBuilder output, double value)
{
// Try to keep the text at 6 characters long to align columns.
if (double.IsNaN(value))
{
output.Append("NaN ");
return;
}
if (value < 0)
{
output.Append("<0 ");
return;
}
value += 0.000005; // Do normal rounding instead of floor/trucate;
var intValue = (int)value;
if (intValue >= 1000000)
{
output.Append("TooBig");
return;
}
var partValue = (int)value;
var digit = partValue / 1000000;
if (intValue >= 1000000)
output.Append((char)(digit + '0'));
// Append the whole part.
for (var p = 1000000; p >= 10;)
{
var n = p / 10;
// Skip leading zeros.
if (intValue >= n)
{
partValue -= digit * p;
digit = partValue / n;
output.Append((char)(digit + '0'));
}
p = n;
}
// Check for room for the '.'.
if (intValue >= 1000000)
return;
if (intValue < 100000)
output.Append('.');
if (intValue >= 10000)
return;
// Up to 5 fractional digits may be appended while keeping the total characters at 6.
var fractionValue = (int)(value * 100000) - 100000 * intValue;
for (var p = 10000; ;)
{
digit = fractionValue / p;
output.Append((char)(digit + '0'));
var n = p / 10;
// Stop if reached 6 characters total.
if (intValue >= n)
return;
fractionValue -= digit * p;
p = n;
}
}
public override void Draw()
{
var cr = Game.Renderer.RgbaColorRenderer;
@@ -56,9 +145,31 @@ namespace OpenRA.Mods.Common.Widgets
}
k = 0;
var maxExtraDotSpace = 0;
var maxNameLength = 0;
foreach (var item in PerfHistory.Items.Values)
{
Game.Renderer.Fonts["Tiny"].DrawText(item.Name, new float2(rect.Left, rect.Top) + new float2(18, 10 * k - 3), Color.White);
maxExtraDotSpace = Math.Max(maxExtraDotSpace, textWidthCache[item.Name]);
maxNameLength = Math.Max(maxNameLength, item.Name.Length);
}
builder ??= new StringBuilder(Math.Max(20, maxExtraDotSpace + 3));
maxExtraDotSpace = Math.Max(0, maxExtraDotSpace - dotWidth); // First dot is always printed.
var columnTwoStart = maxExtraDotSpace + 3 * nextDotAdvance + 2;
foreach (var item in PerfHistory.Items.Values)
{
var nameWidth = textWidthCache[item.Name];
var dotsNeeded = (maxExtraDotSpace - nameWidth) / nextDotAdvance + 3; // first + 2 extra dots for spacing before numbers.
builder.Append(item.Name);
builder.Append('.', dotsNeeded);
font.DrawText(builder.ToString(), new float2(rect.Left, rect.Top) + new float2(18, 10 * k - 3), Color.White);
builder.Clear();
SixCharacterFormatFloat(builder, item.Average(Game.Settings.Debug.Samples));
builder.Append(" : ");
SixCharacterFormatFloat(builder, item.ActiveGameTotalAverage());
font.DrawText(builder.ToString(), new float2(rect.Left, rect.Top) + new float2(18 + columnTwoStart, 10 * k - 3), Color.White);
builder.Clear();
++k;
}
}