From c1b7340023b42161d55993de2e40baad68915b86 Mon Sep 17 00:00:00 2001 From: jduncanator Date: Mon, 29 Oct 2018 09:31:13 +1100 Subject: [PATCH] Timing: Optimize Timestamp Aquisition (#479) * Timing: Optimize Timestamp Aquisition Currently, we make use of Environment.TickCount in a number of places. This has some downsides, mainly being that the TickCount is a signed 32-bit integer, and has an effective limit of ~25 days before overflowing and wrapping around. Due to the signed-ness of the value, this also caused issues with negative numbers. This resolves these issues by using a 64-bit tick count obtained from Performance Counters (via the Stopwatch class). This has a beneficial side effect of being significantly more accurate than the TickCount. * Timing: Rename ElapsedTicks to ElapsedMilliseconds and expose TicksPerX * Timing: Some style changes * Timing: Align static variable initialization --- ChocolArm64/ATranslatorCache.cs | 24 +++---- Ryujinx.Common/PerformanceCounter.cs | 65 +++++++++++++++++++ .../Gal/OpenGL/OGLCachedResource.cs | 21 ++---- Ryujinx.Graphics/NvGpuEngine3d.cs | 3 +- Ryujinx.HLE/HOS/Kernel/KCoreContext.cs | 3 +- Ryujinx.HLE/Hid/Hid.cs | 35 +++++----- 6 files changed, 102 insertions(+), 49 deletions(-) create mode 100644 Ryujinx.Common/PerformanceCounter.cs diff --git a/ChocolArm64/ATranslatorCache.cs b/ChocolArm64/ATranslatorCache.cs index 3e3c5ab654..2d6af90b4e 100644 --- a/ChocolArm64/ATranslatorCache.cs +++ b/ChocolArm64/ATranslatorCache.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics; using System.Runtime.CompilerServices; using System.Threading; @@ -27,7 +28,7 @@ namespace ChocolArm64 public int Size { get; private set; } - public int Timestamp { get; private set; } + public long Timestamp { get; private set; } public CacheBucket(ATranslatedSub Subroutine, LinkedListNode Node, int Size) { @@ -41,7 +42,7 @@ namespace ChocolArm64 { this.Node = Node; - Timestamp = Environment.TickCount; + Timestamp = GetTimestamp(); } } @@ -122,7 +123,7 @@ namespace ChocolArm64 private void ClearCacheIfNeeded() { - int Timestamp = Environment.TickCount; + long Timestamp = GetTimestamp(); while (TotalSize > MaxTotalSize) { @@ -137,9 +138,9 @@ namespace ChocolArm64 CacheBucket Bucket = Cache[Node.Value]; - int TimeDelta = RingDelta(Bucket.Timestamp, Timestamp); + long TimeDelta = Bucket.Timestamp - Timestamp; - if ((uint)TimeDelta <= (uint)MinTimeDelta) + if (TimeDelta <= MinTimeDelta) { break; } @@ -154,16 +155,11 @@ namespace ChocolArm64 } } - private static int RingDelta(int Old, int New) + private static long GetTimestamp() { - if ((uint)New < (uint)Old) - { - return New + (~Old + 1); - } - else - { - return New - Old; - } + long timestamp = Stopwatch.GetTimestamp(); + + return timestamp / (Stopwatch.Frequency / 1000); } } } \ No newline at end of file diff --git a/Ryujinx.Common/PerformanceCounter.cs b/Ryujinx.Common/PerformanceCounter.cs new file mode 100644 index 0000000000..4c8ae6a75a --- /dev/null +++ b/Ryujinx.Common/PerformanceCounter.cs @@ -0,0 +1,65 @@ +using System.Diagnostics; + +namespace Ryujinx.Common +{ + public static class PerformanceCounter + { + /// + /// Represents the number of ticks in 1 day. + /// + public static long TicksPerDay { get; } + + /// + /// Represents the number of ticks in 1 hour. + /// + public static long TicksPerHour { get; } + + /// + /// Represents the number of ticks in 1 minute. + /// + public static long TicksPerMinute { get; } + + /// + /// Represents the number of ticks in 1 second. + /// + public static long TicksPerSecond { get; } + + /// + /// Represents the number of ticks in 1 millisecond. + /// + public static long TicksPerMillisecond { get; } + + /// + /// Gets the number of milliseconds elapsed since the system started. + /// + public static long ElapsedTicks + { + get + { + return Stopwatch.GetTimestamp(); + } + } + + /// + /// Gets the number of milliseconds elapsed since the system started. + /// + public static long ElapsedMilliseconds + { + get + { + long timestamp = Stopwatch.GetTimestamp(); + + return timestamp / TicksPerMillisecond; + } + } + + static PerformanceCounter() + { + TicksPerMillisecond = Stopwatch.Frequency / 1000; + TicksPerSecond = Stopwatch.Frequency; + TicksPerMinute = TicksPerSecond * 60; + TicksPerHour = TicksPerMinute * 60; + TicksPerDay = TicksPerHour * 24; + } + } +} diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLCachedResource.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLCachedResource.cs index 839915eae4..a65ebcbbc2 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLCachedResource.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLCachedResource.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common; using System; using System.Collections.Generic; @@ -18,7 +19,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL public long DataSize { get; private set; } - public int Timestamp { get; private set; } + public long Timestamp { get; private set; } public CacheBucket(T Value, long DataSize, LinkedListNode Node) { @@ -26,7 +27,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL this.DataSize = DataSize; this.Node = Node; - Timestamp = Environment.TickCount; + Timestamp = PerformanceCounter.ElapsedMilliseconds; } } @@ -141,7 +142,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL private void ClearCacheIfNeeded() { - int Timestamp = Environment.TickCount; + long Timestamp = PerformanceCounter.ElapsedMilliseconds; int Count = 0; @@ -156,7 +157,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL CacheBucket Bucket = Cache[Node.Value]; - int TimeDelta = RingDelta(Bucket.Timestamp, Timestamp); + long TimeDelta = Bucket.Timestamp - Timestamp; if ((uint)TimeDelta <= (uint)MaxTimeDelta) { @@ -170,17 +171,5 @@ namespace Ryujinx.Graphics.Gal.OpenGL DeleteValueCallback(Bucket.Value); } } - - private int RingDelta(int Old, int New) - { - if ((uint)New < (uint)Old) - { - return New + (~Old + 1); - } - else - { - return New - Old; - } - } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/NvGpuEngine3d.cs b/Ryujinx.Graphics/NvGpuEngine3d.cs index 7d2b2b4303..618d7d9fad 100644 --- a/Ryujinx.Graphics/NvGpuEngine3d.cs +++ b/Ryujinx.Graphics/NvGpuEngine3d.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common; using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Memory; using Ryujinx.Graphics.Texture; @@ -851,7 +852,7 @@ namespace Ryujinx.Graphics //TODO: Implement counters. long Counter = 1; - long Timestamp = (uint)Environment.TickCount; + long Timestamp = PerformanceCounter.ElapsedMilliseconds; Timestamp = (long)(Timestamp * 615384.615385); diff --git a/Ryujinx.HLE/HOS/Kernel/KCoreContext.cs b/Ryujinx.HLE/HOS/Kernel/KCoreContext.cs index 51f27e2a1e..02354e16ad 100644 --- a/Ryujinx.HLE/HOS/Kernel/KCoreContext.cs +++ b/Ryujinx.HLE/HOS/Kernel/KCoreContext.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common; using System; namespace Ryujinx.HLE.HOS.Kernel @@ -25,7 +26,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (Thread != null) { - Thread.LastScheduledTicks = (uint)Environment.TickCount; + Thread.LastScheduledTicks = PerformanceCounter.ElapsedMilliseconds; } if (SelectedThread != CurrentThread) diff --git a/Ryujinx.HLE/Hid/Hid.cs b/Ryujinx.HLE/Hid/Hid.cs index 66b38db595..21580223e7 100644 --- a/Ryujinx.HLE/Hid/Hid.cs +++ b/Ryujinx.HLE/Hid/Hid.cs @@ -1,4 +1,5 @@ -using Ryujinx.HLE.HOS; +using Ryujinx.Common; +using Ryujinx.HLE.HOS; using System; namespace Ryujinx.HLE.Input @@ -98,12 +99,12 @@ namespace Ryujinx.HLE.Input HidControllerColorDesc SplitColorDesc = 0; - Device.Memory.WriteInt32(BaseControllerOffset + 0x0, (int)Type); + Device.Memory.WriteInt32(BaseControllerOffset + 0x00, (int)Type); - Device.Memory.WriteInt32(BaseControllerOffset + 0x4, IsHalf ? 1 : 0); + Device.Memory.WriteInt32(BaseControllerOffset + 0x04, IsHalf ? 1 : 0); - Device.Memory.WriteInt32(BaseControllerOffset + 0x8, (int)SingleColorDesc); - Device.Memory.WriteInt32(BaseControllerOffset + 0xc, (int)SingleColorBody); + Device.Memory.WriteInt32(BaseControllerOffset + 0x08, (int)SingleColorDesc); + Device.Memory.WriteInt32(BaseControllerOffset + 0x0c, (int)SingleColorBody); Device.Memory.WriteInt32(BaseControllerOffset + 0x10, (int)SingleColorButtons); Device.Memory.WriteInt32(BaseControllerOffset + 0x14, (int)SplitColorDesc); @@ -186,8 +187,8 @@ namespace Ryujinx.HLE.Input long Timestamp = GetTimestamp(); - Device.Memory.WriteInt64(ControllerOffset + 0x0, Timestamp); - Device.Memory.WriteInt64(ControllerOffset + 0x8, HidEntryCount); + Device.Memory.WriteInt64(ControllerOffset + 0x00, Timestamp); + Device.Memory.WriteInt64(ControllerOffset + 0x08, HidEntryCount); Device.Memory.WriteInt64(ControllerOffset + 0x10, CurrEntry); Device.Memory.WriteInt64(ControllerOffset + 0x18, HidEntryCount - 1); @@ -199,8 +200,8 @@ namespace Ryujinx.HLE.Input long SampleCounter = Device.Memory.ReadInt64(LastEntryOffset) + 1; - Device.Memory.WriteInt64(ControllerOffset + 0x0, SampleCounter); - Device.Memory.WriteInt64(ControllerOffset + 0x8, SampleCounter); + Device.Memory.WriteInt64(ControllerOffset + 0x00, SampleCounter); + Device.Memory.WriteInt64(ControllerOffset + 0x08, SampleCounter); Device.Memory.WriteInt64(ControllerOffset + 0x10, (uint)Buttons); @@ -225,8 +226,8 @@ namespace Ryujinx.HLE.Input long Timestamp = GetTimestamp(); - Device.Memory.WriteInt64(TouchScreenOffset + 0x0, Timestamp); - Device.Memory.WriteInt64(TouchScreenOffset + 0x8, HidEntryCount); + Device.Memory.WriteInt64(TouchScreenOffset + 0x00, Timestamp); + Device.Memory.WriteInt64(TouchScreenOffset + 0x08, HidEntryCount); Device.Memory.WriteInt64(TouchScreenOffset + 0x10, CurrEntry); Device.Memory.WriteInt64(TouchScreenOffset + 0x18, HidEntryCount - 1); Device.Memory.WriteInt64(TouchScreenOffset + 0x20, Timestamp); @@ -239,8 +240,8 @@ namespace Ryujinx.HLE.Input TouchEntryOffset += CurrEntry * HidTouchEntrySize; - Device.Memory.WriteInt64(TouchEntryOffset + 0x0, SampleCounter); - Device.Memory.WriteInt64(TouchEntryOffset + 0x8, Points.Length); + Device.Memory.WriteInt64(TouchEntryOffset + 0x00, SampleCounter); + Device.Memory.WriteInt64(TouchEntryOffset + 0x08, Points.Length); TouchEntryOffset += HidTouchEntryHeaderSize; @@ -250,9 +251,9 @@ namespace Ryujinx.HLE.Input foreach (HidTouchPoint Point in Points) { - Device.Memory.WriteInt64(TouchEntryOffset + 0x0, Timestamp); - Device.Memory.WriteInt32(TouchEntryOffset + 0x8, Padding); - Device.Memory.WriteInt32(TouchEntryOffset + 0xc, Index++); + Device.Memory.WriteInt64(TouchEntryOffset + 0x00, Timestamp); + Device.Memory.WriteInt32(TouchEntryOffset + 0x08, Padding); + Device.Memory.WriteInt32(TouchEntryOffset + 0x0c, Index++); Device.Memory.WriteInt32(TouchEntryOffset + 0x10, Point.X); Device.Memory.WriteInt32(TouchEntryOffset + 0x14, Point.Y); Device.Memory.WriteInt32(TouchEntryOffset + 0x18, Point.DiameterX); @@ -266,7 +267,7 @@ namespace Ryujinx.HLE.Input private static long GetTimestamp() { - return (long)((ulong)Environment.TickCount * 19_200); + return PerformanceCounter.ElapsedMilliseconds * 19200; } } }