diff --git a/OpenRa.Game/Game.cs b/OpenRa.Game/Game.cs index 64f0d56a00..dda2b8a277 100644 --- a/OpenRa.Game/Game.cs +++ b/OpenRa.Game/Game.cs @@ -9,6 +9,7 @@ using OpenRa.FileFormats; using OpenRa.Game.GameRules; using OpenRa.Game.Graphics; using OpenRa.Game.Traits; +using OpenRa.Game.Support; namespace OpenRa.Game { @@ -158,32 +159,37 @@ namespace OpenRa.Game int dt = t - lastTime; if( dt >= timestep ) { - sw.Reset(); - PathToPathTime = 0; - NormalPathTime = 0; - PathToPathCount = 0; - NormalPathCount = 0; - lastTime += timestep; - - if( orderManager.Tick() ) + using (new PerfSample("tick_time")) { - if( controller.orderGenerator != null ) - controller.orderGenerator.Tick(); + sw.Reset(); + PathToPathTime = 0; + NormalPathTime = 0; + PathToPathCount = 0; + NormalPathCount = 0; + lastTime += timestep; - if (--oreTicks == 0) + if (orderManager.Tick()) { - var oresw = new Stopwatch(); - map.GrowOre(SharedRandom); - OreTime = oresw.ElapsedTime(); - oreTicks = oreFrequency; + if (controller.orderGenerator != null) + controller.orderGenerator.Tick(); + + if (--oreTicks == 0) + { + var oresw = new Stopwatch(); + map.GrowOre(SharedRandom); + OreTime = oresw.ElapsedTime(); + oreTicks = oreFrequency; + } + world.Tick(); + UnitInfluence.Tick(); + foreach (var player in players.Values) + player.Tick(); } - world.Tick(); - UnitInfluence.Tick(); - foreach( var player in players.Values ) - player.Tick(); + + TickTime = sw.ElapsedTime(); } - TickTime = sw.ElapsedTime(); + PerfHistory.Tick(); } sw.Reset(); diff --git a/OpenRa.Game/Graphics/WorldRenderer.cs b/OpenRa.Game/Graphics/WorldRenderer.cs index 9894f4c34c..32d24f3bca 100644 --- a/OpenRa.Game/Graphics/WorldRenderer.cs +++ b/OpenRa.Game/Graphics/WorldRenderer.cs @@ -3,7 +3,8 @@ using System.Linq; using System.Windows.Forms; using IjwFramework.Types; using System.Collections.Generic; -using OpenRa.Game.Traits; +using OpenRa.Game.Traits; +using OpenRa.Game.Support; namespace OpenRa.Game.Graphics { @@ -109,7 +110,9 @@ namespace OpenRa.Game.Graphics Game.LocalPlayer.Cash, Game.PathToPathCount, Game.NormalPathCount - ), new int2(5, 5), Color.White); + ), new int2(5, 5), Color.White); + + PerfHistory.Render(renderer, lineRenderer); } void DrawSelectionBox(Actor selectedUnit, Color c, bool drawHealthBar) diff --git a/OpenRa.Game/OpenRa.Game.csproj b/OpenRa.Game/OpenRa.Game.csproj index 5208f20535..a620d9e68f 100644 --- a/OpenRa.Game/OpenRa.Game.csproj +++ b/OpenRa.Game/OpenRa.Game.csproj @@ -79,6 +79,7 @@ + diff --git a/OpenRa.Game/PathFinder.cs b/OpenRa.Game/PathFinder.cs index fb32afc68d..4aa1015aaa 100644 --- a/OpenRa.Game/PathFinder.cs +++ b/OpenRa.Game/PathFinder.cs @@ -4,6 +4,7 @@ using IjwFramework.Collections; using System.Linq; using OpenRa.FileFormats; using OpenRa.Game.Graphics; +using OpenRa.Game.Support; namespace OpenRa.Game { @@ -28,16 +29,19 @@ namespace OpenRa.Game public List FindUnitPath(int2 src, int2 dest, UnitMovementType umt) { - var sw = new Stopwatch(); - /*if (passableCost[(int)umt][dest.X, dest.Y] == double.PositiveInfinity) - return new List(); - if (!Game.BuildingInfluence.CanMoveHere(dest)) - return new List();*/ + using (new PerfSample("find_unit_path")) + { + var sw = new Stopwatch(); + /*if (passableCost[(int)umt][dest.X, dest.Y] == double.PositiveInfinity) + return new List(); + if (!Game.BuildingInfluence.CanMoveHere(dest)) + return new List();*/ - var result = FindUnitPath(src, DefaultEstimator(dest), umt); - Game.NormalPathTime += sw.ElapsedTime(); - Game.NormalPathCount++; - return result; + var result = FindUnitPath(src, DefaultEstimator(dest), umt); + Game.NormalPathTime += sw.ElapsedTime(); + Game.NormalPathCount++; + return result; + } } public List FindUnitPathToRange(int2 src, int2 dest, UnitMovementType umt, int range) @@ -53,30 +57,33 @@ namespace OpenRa.Game public List FindPathToPath( int2 from, List path, UnitMovementType umt ) { var sw = new Stopwatch(); - - var cellInfo = InitCellInfo(); - var queue = new PriorityQueue(); - var estimator = DefaultEstimator( from ); - - var cost = 0.0; - var prev = path[ 0 ]; - for( int i = 0 ; i < path.Count ; i++ ) + using (new PerfSample("find_path_to_path")) { - var sl = path[ i ]; - if( /*i == 0 || */(Game.BuildingInfluence.CanMoveHere(path[i]) && Game.UnitInfluence.GetUnitAt( path[ i ] ) == null) ) + + var cellInfo = InitCellInfo(); + var queue = new PriorityQueue(); + var estimator = DefaultEstimator(from); + + var cost = 0.0; + var prev = path[0]; + for (int i = 0; i < path.Count; i++) { - queue.Add( new PathDistance( estimator( sl ), sl ) ); - cellInfo[ sl.X, sl.Y ] = new CellInfo( cost, prev, false ); + var sl = path[i]; + if ( /*i == 0 || */(Game.BuildingInfluence.CanMoveHere(path[i]) && Game.UnitInfluence.GetUnitAt(path[i]) == null)) + { + queue.Add(new PathDistance(estimator(sl), sl)); + cellInfo[sl.X, sl.Y] = new CellInfo(cost, prev, false); + } + var d = sl - prev; + cost += ((d.X * d.Y != 0) ? 1.414213563 : 1.0) * passableCost[(int)umt][sl.X, sl.Y]; + prev = sl; } - var d = sl - prev; - cost += ( ( d.X * d.Y != 0 ) ? 1.414213563 : 1.0 ) * passableCost[ (int)umt ][ sl.X, sl.Y ]; - prev = sl; + var ret = FindPath(cellInfo, queue, estimator, umt, true); + ret.Reverse(); + Game.PathToPathTime += sw.ElapsedTime(); + Game.PathToPathCount++; + return ret; } - var ret = FindPath( cellInfo, queue, estimator, umt, true ); - ret.Reverse(); - Game.PathToPathTime += sw.ElapsedTime(); - Game.PathToPathCount++; - return ret; } public List FindUnitPath( int2 unitLocation, Func estimator, UnitMovementType umt ) diff --git a/OpenRa.Game/Support/PerfHistory.cs b/OpenRa.Game/Support/PerfHistory.cs new file mode 100644 index 0000000000..c4df92dd22 --- /dev/null +++ b/OpenRa.Game/Support/PerfHistory.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using IjwFramework.Collections; +using System.Drawing; +using OpenRa.Game.Graphics; + +namespace OpenRa.Game.Support +{ + static class PerfHistory + { + static readonly Color[] colors = { Color.Red, Color.Green, Color.Blue, Color.Yellow, Color.Orange, Color.Fuchsia, Color.Lime, Color.LightBlue }; + static int nextColor; + + static Cache items = new Cache( + s => + { + var x = new PerfItem(s, colors[nextColor++]); + if (nextColor > colors.Length) nextColor = 0; + return x; + }); + + public static void Increment( string item, double x ) + { + items[item].val += x; + } + + public static void Tick() + { + foreach (var item in items.Values) + item.Tick(); + } + + public static void Render(Renderer r, LineRenderer lr) + { + float2 origin = Game.viewport.Location + new float2(330, Game.viewport.Height - 30); + float2 basis = new float2(-3, -3); + + lr.DrawLine(origin, origin + new float2(100, 0) * basis, Color.White, Color.White); + lr.DrawLine(origin + new float2(100,0) * basis, origin + new float2(100,70) * basis, Color.White, Color.White); + + foreach (var item in items.Values) + { + int n = 0; + item.Samples().Aggregate((a, b) => + { + lr.DrawLine( + origin + new float2(n, (float)a) * basis, + origin + new float2(n+1, (float)b) * basis, + item.c, item.c); + ++n; + return b; + }); + } + + lr.Flush(); + } + } + + class PerfItem + { + public readonly Color c; + public readonly string Name; + public double[] samples = new double[100]; + public double val = 0.0; + int head = 1, tail = 0; + + public PerfItem(string name, Color c) + { + Name = name; + this.c = c; + } + + public void Tick() + { + samples[head++] = val; + if (head == samples.Length) head = 0; + if (head == tail && ++tail == samples.Length) tail = 0; + val = 0.0; + } + + public IEnumerable Samples() + { + int n = head; + while (n != tail) + { + --n; + if (n < 0) n = samples.Length - 1; + yield return samples[n]; + } + } + } + + class PerfSample : IDisposable + { + readonly Stopwatch sw = new Stopwatch(); + readonly string Item; + + public PerfSample(string item) + { + Item = item; + } + + public void Dispose() + { + PerfHistory.Increment(Item, sw.ElapsedTime() * 1000); + } + } +}