mirror of
https://github.com/Ellpeck/MLEM.git
synced 2024-11-26 06:28:35 +01:00
Added UiMetrics
This commit is contained in:
parent
55fae16768
commit
60dfbb1ec5
5 changed files with 164 additions and 8 deletions
|
@ -38,6 +38,7 @@ Additions
|
||||||
- Added a multiline editing mode to TextField
|
- Added a multiline editing mode to TextField
|
||||||
- Added a formatting code to allow for inline font changes
|
- Added a formatting code to allow for inline font changes
|
||||||
- Added a SquishingGroup element
|
- Added a SquishingGroup element
|
||||||
|
- Added UiMetrics
|
||||||
|
|
||||||
Improvements
|
Improvements
|
||||||
- **Made Image ScaleToImage take ui scale into account**
|
- **Made Image ScaleToImage take ui scale into account**
|
||||||
|
|
|
@ -18,6 +18,8 @@ namespace Demos {
|
||||||
private double fpsTime;
|
private double fpsTime;
|
||||||
private int lastFps;
|
private int lastFps;
|
||||||
private int fpsCounter;
|
private int fpsCounter;
|
||||||
|
private UiMetrics cumulativeMetrics;
|
||||||
|
private TimeSpan secondCounter;
|
||||||
|
|
||||||
static GameImpl() {
|
static GameImpl() {
|
||||||
Demos.Add("Ui", ("An in-depth demonstration of the MLEM.Ui package and its abilities", game => new UiDemo(game)));
|
Demos.Add("Ui", ("An in-depth demonstration of the MLEM.Ui package and its abilities", game => new UiDemo(game)));
|
||||||
|
@ -29,6 +31,16 @@ namespace Demos {
|
||||||
|
|
||||||
public GameImpl() {
|
public GameImpl() {
|
||||||
this.IsMouseVisible = true;
|
this.IsMouseVisible = true;
|
||||||
|
// print out ui metrics every second
|
||||||
|
this.OnDraw += (g, time) => {
|
||||||
|
this.cumulativeMetrics += this.UiSystem.Metrics;
|
||||||
|
this.secondCounter += time.ElapsedGameTime;
|
||||||
|
if (this.secondCounter.TotalSeconds >= 1) {
|
||||||
|
this.secondCounter -= TimeSpan.FromSeconds(1);
|
||||||
|
Console.WriteLine($"Metrics/s: {this.cumulativeMetrics}");
|
||||||
|
this.cumulativeMetrics = new UiMetrics();
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadContent() {
|
protected override void LoadContent() {
|
||||||
|
|
|
@ -546,6 +546,7 @@ namespace MLEM.Ui.Elements {
|
||||||
// which would cause our ForceUpdateArea code to be run twice, so we only update our parent instead
|
// which would cause our ForceUpdateArea code to be run twice, so we only update our parent instead
|
||||||
if (this.Parent != null && this.Parent.UpdateAreaIfDirty())
|
if (this.Parent != null && this.Parent.UpdateAreaIfDirty())
|
||||||
return;
|
return;
|
||||||
|
this.System.Stopwatch.Restart();
|
||||||
|
|
||||||
var parentArea = this.Parent != null ? this.Parent.ChildPaddedArea : (RectangleF) this.system.Viewport;
|
var parentArea = this.Parent != null ? this.Parent.ChildPaddedArea : (RectangleF) this.system.Viewport;
|
||||||
var parentCenterX = parentArea.X + parentArea.Width / 2;
|
var parentCenterX = parentArea.X + parentArea.Width / 2;
|
||||||
|
@ -555,6 +556,10 @@ namespace MLEM.Ui.Elements {
|
||||||
var recursion = 0;
|
var recursion = 0;
|
||||||
UpdateDisplayArea(actualSize);
|
UpdateDisplayArea(actualSize);
|
||||||
|
|
||||||
|
this.System.Stopwatch.Stop();
|
||||||
|
this.System.Metrics.ForceAreaUpdateTime += this.System.Stopwatch.Elapsed;
|
||||||
|
this.System.Metrics.ForceAreaUpdates++;
|
||||||
|
|
||||||
void UpdateDisplayArea(Vector2 newSize) {
|
void UpdateDisplayArea(Vector2 newSize) {
|
||||||
var pos = new Vector2();
|
var pos = new Vector2();
|
||||||
switch (this.anchor) {
|
switch (this.anchor) {
|
||||||
|
@ -706,6 +711,7 @@ namespace MLEM.Ui.Elements {
|
||||||
this.System.InvokeOnElementAreaUpdated(this);
|
this.System.InvokeOnElementAreaUpdated(this);
|
||||||
foreach (var child in this.Children)
|
foreach (var child in this.Children)
|
||||||
child.ForceUpdateArea();
|
child.ForceUpdateArea();
|
||||||
|
this.System.Metrics.ActualAreaUpdates++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -885,6 +891,8 @@ namespace MLEM.Ui.Elements {
|
||||||
foreach (var child in this.GetRelevantChildren())
|
foreach (var child in this.GetRelevantChildren())
|
||||||
if (child.System != null)
|
if (child.System != null)
|
||||||
child.Update(time);
|
child.Update(time);
|
||||||
|
|
||||||
|
this.System.Metrics.Updates++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -914,6 +922,7 @@ namespace MLEM.Ui.Elements {
|
||||||
}
|
}
|
||||||
// draw content in custom begin call
|
// draw content in custom begin call
|
||||||
this.Draw(time, batch, alpha, blendState, samplerState, depthStencilState, effect, mat);
|
this.Draw(time, batch, alpha, blendState, samplerState, depthStencilState, effect, mat);
|
||||||
|
this.System.Metrics.Draws++;
|
||||||
if (customDraw) {
|
if (customDraw) {
|
||||||
// end our draw
|
// end our draw
|
||||||
batch.End();
|
batch.End();
|
||||||
|
|
107
MLEM.Ui/UiMetrics.cs
Normal file
107
MLEM.Ui/UiMetrics.cs
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
using System;
|
||||||
|
using Microsoft.Xna.Framework.Graphics;
|
||||||
|
using MLEM.Ui.Elements;
|
||||||
|
|
||||||
|
namespace MLEM.Ui {
|
||||||
|
/// <summary>
|
||||||
|
/// A snapshot of update and rendering statistics from <see cref="UiSystem.Metrics"/> to be used for runtime debugging and profiling.
|
||||||
|
/// This metrics struct works similarly to <see cref="GraphicsMetrics"/>.
|
||||||
|
/// </summary>
|
||||||
|
public struct UiMetrics {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of time that <see cref="Element.ForceUpdateArea"/> took.
|
||||||
|
/// Can be divided by <see cref="ForceAreaUpdates"/> to get an average per area update.
|
||||||
|
/// </summary>
|
||||||
|
public TimeSpan ForceAreaUpdateTime { get; internal set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of time that <see cref="Element.Update"/> took.
|
||||||
|
/// Can be divided by <see cref="Updates"/> to get an average per update.
|
||||||
|
/// </summary>
|
||||||
|
public TimeSpan UpdateTime { get; internal set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of times that <see cref="Element.ForceUpdateArea"/> was called.
|
||||||
|
/// </summary>
|
||||||
|
public uint ForceAreaUpdates { get; internal set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of times that <see cref="Element.SetAreaAndUpdateChildren"/> was called.
|
||||||
|
/// </summary>
|
||||||
|
public uint ActualAreaUpdates { get; internal set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of times that <see cref="Element.Update"/> was called.
|
||||||
|
/// </summary>
|
||||||
|
public uint Updates { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of time that <see cref="Element.Draw"/> took.
|
||||||
|
/// Can be divided by <see cref="Draws"/> to get an average per draw.
|
||||||
|
/// </summary>
|
||||||
|
public TimeSpan DrawTime { get; internal set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of times that <see cref="Element.Draw"/> was called.
|
||||||
|
/// </summary>
|
||||||
|
public uint Draws { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resets all update-related metrics to 0.
|
||||||
|
/// </summary>
|
||||||
|
public void ResetUpdates() {
|
||||||
|
this.ForceAreaUpdateTime = TimeSpan.Zero;
|
||||||
|
this.UpdateTime = TimeSpan.Zero;
|
||||||
|
this.ForceAreaUpdates = 0;
|
||||||
|
this.ActualAreaUpdates = 0;
|
||||||
|
this.Updates = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resets all rendering-related metrics to 0.
|
||||||
|
/// </summary>
|
||||||
|
public void ResetDraws() {
|
||||||
|
this.DrawTime = TimeSpan.Zero;
|
||||||
|
this.Draws = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns the fully qualified type name of this instance.</summary>
|
||||||
|
/// <returns>The fully qualified type name.</returns>
|
||||||
|
public override string ToString() {
|
||||||
|
return $"{nameof(this.ForceAreaUpdateTime)}: {this.ForceAreaUpdateTime}, {nameof(this.UpdateTime)}: {this.UpdateTime}, {nameof(this.ForceAreaUpdates)}: {this.ForceAreaUpdates}, {nameof(this.ActualAreaUpdates)}: {this.ActualAreaUpdates}, {nameof(this.Updates)}: {this.Updates}, {nameof(this.DrawTime)}: {this.DrawTime}, {nameof(this.Draws)}: {this.Draws}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds two ui metrics together, causing all of their values to be combined.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left">The left metrics</param>
|
||||||
|
/// <param name="right">The right metrics</param>
|
||||||
|
/// <returns>The sum of both metrics</returns>
|
||||||
|
public static UiMetrics operator +(UiMetrics left, UiMetrics right) {
|
||||||
|
return new UiMetrics {
|
||||||
|
ForceAreaUpdateTime = left.ForceAreaUpdateTime + right.ForceAreaUpdateTime,
|
||||||
|
UpdateTime = left.UpdateTime + right.UpdateTime,
|
||||||
|
ForceAreaUpdates = left.ForceAreaUpdates + right.ForceAreaUpdates,
|
||||||
|
ActualAreaUpdates = left.ActualAreaUpdates + right.ActualAreaUpdates,
|
||||||
|
Updates = left.Updates + right.Updates,
|
||||||
|
DrawTime = left.DrawTime + right.DrawTime,
|
||||||
|
Draws = left.Draws + right.Draws
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Subtracts two ui metrics from each other, causing their values to be subtracted.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left">The left metrics</param>
|
||||||
|
/// <param name="right">The right metrics</param>
|
||||||
|
/// <returns>The difference of both metrics</returns>
|
||||||
|
public static UiMetrics operator -(UiMetrics left, UiMetrics right) {
|
||||||
|
return new UiMetrics {
|
||||||
|
ForceAreaUpdateTime = left.ForceAreaUpdateTime - right.ForceAreaUpdateTime,
|
||||||
|
UpdateTime = left.UpdateTime - right.UpdateTime,
|
||||||
|
ForceAreaUpdates = left.ForceAreaUpdates - right.ForceAreaUpdates,
|
||||||
|
ActualAreaUpdates = left.ActualAreaUpdates - right.ActualAreaUpdates,
|
||||||
|
Updates = left.Updates - right.Updates,
|
||||||
|
DrawTime = left.DrawTime - right.DrawTime,
|
||||||
|
Draws = left.Draws - right.Draws
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
|
@ -20,8 +21,6 @@ namespace MLEM.Ui {
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class UiSystem : GameComponent {
|
public class UiSystem : GameComponent {
|
||||||
|
|
||||||
private readonly List<RootElement> rootElements = new List<RootElement>();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The viewport that this ui system is rendering inside of.
|
/// The viewport that this ui system is rendering inside of.
|
||||||
/// This is automatically updated during <see cref="GameWindow.ClientSizeChanged"/>
|
/// This is automatically updated during <see cref="GameWindow.ClientSizeChanged"/>
|
||||||
|
@ -36,7 +35,6 @@ namespace MLEM.Ui {
|
||||||
/// If <see cref="AutoScaleWithScreen"/> is true, this is used as the screen size that uses the default <see cref="GlobalScale"/>
|
/// If <see cref="AutoScaleWithScreen"/> is true, this is used as the screen size that uses the default <see cref="GlobalScale"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Point AutoScaleReferenceSize;
|
public Point AutoScaleReferenceSize;
|
||||||
private float globalScale = 1;
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The global rendering scale of this ui system and all of its child elements.
|
/// The global rendering scale of this ui system and all of its child elements.
|
||||||
/// If <see cref="AutoScaleWithScreen"/> is true, this scale will be different based on the window size.
|
/// If <see cref="AutoScaleWithScreen"/> is true, this scale will be different based on the window size.
|
||||||
|
@ -53,8 +51,6 @@ namespace MLEM.Ui {
|
||||||
root.Element.ForceUpdateArea();
|
root.Element.ForceUpdateArea();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private UiStyle style;
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The style options that this ui system and all of its elements use.
|
/// The style options that this ui system and all of its elements use.
|
||||||
/// To set the default, untextured style, use <see cref="UntexturedStyle"/>.
|
/// To set the default, untextured style, use <see cref="UntexturedStyle"/>.
|
||||||
|
@ -102,6 +98,11 @@ namespace MLEM.Ui {
|
||||||
/// The ui controls are also the place to change bindings for controller and keyboard input.
|
/// The ui controls are also the place to change bindings for controller and keyboard input.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public UiControls Controls;
|
public UiControls Controls;
|
||||||
|
/// <summary>
|
||||||
|
/// The update and rendering statistics to be used for runtime debugging and profiling.
|
||||||
|
/// The metrics are reset accordingly every frame: <see cref="UiMetrics.ResetUpdates"/> is called at the start of <see cref="Update"/>, and <see cref="UiMetrics.ResetDraws"/> is called at the start of <see cref="DrawEarly"/>, or at the start of <see cref="Draw"/> if <see cref="DrawEarly"/> was not called.
|
||||||
|
/// </summary>
|
||||||
|
public UiMetrics Metrics;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event that is invoked after an <see cref="Element"/> is drawn, but before its children are drawn.
|
/// Event that is invoked after an <see cref="Element"/> is drawn, but before its children are drawn.
|
||||||
|
@ -172,6 +173,13 @@ namespace MLEM.Ui {
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event RootCallback OnRootRemoved;
|
public event RootCallback OnRootRemoved;
|
||||||
|
|
||||||
|
internal readonly Stopwatch Stopwatch = new Stopwatch();
|
||||||
|
|
||||||
|
private readonly List<RootElement> rootElements = new List<RootElement>();
|
||||||
|
private float globalScale = 1;
|
||||||
|
private bool drewEarly;
|
||||||
|
private UiStyle style;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new ui system with the given settings.
|
/// Creates a new ui system with the given settings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -232,11 +240,15 @@ namespace MLEM.Ui {
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="time">The game's time</param>
|
/// <param name="time">The game's time</param>
|
||||||
public override void Update(GameTime time) {
|
public override void Update(GameTime time) {
|
||||||
this.Controls.Update();
|
this.Metrics.ResetUpdates();
|
||||||
|
this.Stopwatch.Restart();
|
||||||
|
|
||||||
for (var i = this.rootElements.Count - 1; i >= 0; i--) {
|
this.Controls.Update();
|
||||||
|
for (var i = this.rootElements.Count - 1; i >= 0; i--)
|
||||||
this.rootElements[i].Element.Update(time);
|
this.rootElements[i].Element.Update(time);
|
||||||
}
|
|
||||||
|
this.Stopwatch.Stop();
|
||||||
|
this.Metrics.UpdateTime += this.Stopwatch.Elapsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -246,10 +258,17 @@ namespace MLEM.Ui {
|
||||||
/// <param name="time">The game's time</param>
|
/// <param name="time">The game's time</param>
|
||||||
/// <param name="batch">The sprite batch to use for drawing</param>
|
/// <param name="batch">The sprite batch to use for drawing</param>
|
||||||
public void DrawEarly(GameTime time, SpriteBatch batch) {
|
public void DrawEarly(GameTime time, SpriteBatch batch) {
|
||||||
|
this.Metrics.ResetDraws();
|
||||||
|
this.Stopwatch.Restart();
|
||||||
|
|
||||||
foreach (var root in this.rootElements) {
|
foreach (var root in this.rootElements) {
|
||||||
if (!root.Element.IsHidden)
|
if (!root.Element.IsHidden)
|
||||||
root.Element.DrawEarly(time, batch, this.DrawAlpha * root.Element.DrawAlpha, this.BlendState, this.SamplerState, this.DepthStencilState, this.Effect, root.Transform);
|
root.Element.DrawEarly(time, batch, this.DrawAlpha * root.Element.DrawAlpha, this.BlendState, this.SamplerState, this.DepthStencilState, this.Effect, root.Transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.Stopwatch.Stop();
|
||||||
|
this.Metrics.DrawTime += this.Stopwatch.Elapsed;
|
||||||
|
this.drewEarly = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -259,6 +278,10 @@ namespace MLEM.Ui {
|
||||||
/// <param name="time">The game's time</param>
|
/// <param name="time">The game's time</param>
|
||||||
/// <param name="batch">The sprite batch to use for drawing</param>
|
/// <param name="batch">The sprite batch to use for drawing</param>
|
||||||
public void Draw(GameTime time, SpriteBatch batch) {
|
public void Draw(GameTime time, SpriteBatch batch) {
|
||||||
|
if (!this.drewEarly)
|
||||||
|
this.Metrics.ResetDraws();
|
||||||
|
this.Stopwatch.Restart();
|
||||||
|
|
||||||
foreach (var root in this.rootElements) {
|
foreach (var root in this.rootElements) {
|
||||||
if (root.Element.IsHidden)
|
if (root.Element.IsHidden)
|
||||||
continue;
|
continue;
|
||||||
|
@ -267,6 +290,10 @@ namespace MLEM.Ui {
|
||||||
root.Element.DrawTransformed(time, batch, alpha, this.BlendState, this.SamplerState, this.DepthStencilState, this.Effect, root.Transform);
|
root.Element.DrawTransformed(time, batch, alpha, this.BlendState, this.SamplerState, this.DepthStencilState, this.Effect, root.Transform);
|
||||||
batch.End();
|
batch.End();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.Stopwatch.Stop();
|
||||||
|
this.Metrics.DrawTime += this.Stopwatch.Elapsed;
|
||||||
|
this.drewEarly = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
Loading…
Reference in a new issue