From 494fe179220c08fd5081d2eaf6bfd06f8c3ce413 Mon Sep 17 00:00:00 2001 From: Highbyte Date: Tue, 28 Nov 2023 21:16:45 +0100 Subject: [PATCH 01/10] Refactor stats to be less of a mess --- .../Program.cs | 2 +- .../Instrumentation/InstrumentationBag.cs | 18 --- .../Instrumentation/Stats/AveragedStat.cs | 26 ----- .../Stats/DisposableCallback.cs | 11 -- .../Stats/ElapsedMillisecondsStat.cs | 35 ------ .../Instrumentation/Stats/IStat.cs | 9 -- .../Stats/PerSecondTimedStat.cs | 43 -------- .../SilkNetImGuiStatsPanel.cs | 12 +- .../SilkNetWindow.cs | 104 +++++------------- .../Instrumentation/InstrumentationBag.cs | 19 ---- .../Stats/ElapsedMillisecondsTimedStat.cs | 41 ------- .../Skia/WasmHost.cs | 101 ++++------------- .../Commodore64/Audio/C64WASMAudioHandler.cs | 13 +-- .../Input/C64AspNetInputHandler.cs | 4 + .../GenericComputerAspNetInputHandler.cs | 12 +- .../Audio/C64NAudioAudioHandler.cs | 12 +- .../Input/C64SadConsoleInputHandler.cs | 4 + .../Video/C64SadConsoleRenderer.cs | 11 +- .../Input/GenericSadConsoleInputHandler.cs | 12 +- .../Video/GenericSadConsoleRenderer.cs | 10 +- .../SadConsoleMain.cs | 4 +- .../Input/C64SilkNetInputHandler.cs | 12 +- .../Video/C64SilkNetOpenGlRenderer.cs | 16 +-- .../GenericComputerSilkNetInputHandler.cs | 11 +- .../Commodore64/Video/C64SkiaRenderer.cs | 99 +++++++---------- .../Video/GenericComputerSkiaRenderer.cs | 11 +- .../Commodore64/C64.cs | 45 ++++---- .../Generic/GenericComputer.cs | 16 +-- .../Instrumentation/InstrumentationBag.cs | 18 +++ .../Instrumentation/Instrumentations.cs | 17 +++ .../Instrumentation/Stats/AveragedStat.cs | 12 +- .../Stats/DisposableCallback.cs | 2 +- .../Stats/ElapsedMillisecondsStat.cs | 10 +- .../Stats/ElapsedMillisecondsTimedStat.cs | 29 +++-- .../Instrumentation/Stats/IStat.cs | 2 +- .../Stats/PerSecondTimedStat.cs | 14 +-- .../Systems/IAudioHandler.cs | 10 +- .../Systems/IInputHandler.cs | 7 +- .../Highbyte.DotNet6502/Systems/IRenderer.cs | 15 ++- .../Highbyte.DotNet6502/Systems/ISystem.cs | 11 +- .../Systems/SystemRunner.cs | 31 ++---- .../Systems/SystemRunnerBuilderTests.cs | 50 ++++----- 42 files changed, 319 insertions(+), 622 deletions(-) delete mode 100644 src/apps/Highbyte.DotNet6502.App.SilkNetNative/Instrumentation/InstrumentationBag.cs delete mode 100644 src/apps/Highbyte.DotNet6502.App.SilkNetNative/Instrumentation/Stats/AveragedStat.cs delete mode 100644 src/apps/Highbyte.DotNet6502.App.SilkNetNative/Instrumentation/Stats/DisposableCallback.cs delete mode 100644 src/apps/Highbyte.DotNet6502.App.SilkNetNative/Instrumentation/Stats/ElapsedMillisecondsStat.cs delete mode 100644 src/apps/Highbyte.DotNet6502.App.SilkNetNative/Instrumentation/Stats/IStat.cs delete mode 100644 src/apps/Highbyte.DotNet6502.App.SilkNetNative/Instrumentation/Stats/PerSecondTimedStat.cs delete mode 100644 src/apps/Highbyte.DotNet6502.App.WASM/Instrumentation/InstrumentationBag.cs delete mode 100644 src/apps/Highbyte.DotNet6502.App.WASM/Instrumentation/Stats/ElapsedMillisecondsTimedStat.cs create mode 100644 src/libraries/Highbyte.DotNet6502/Instrumentation/InstrumentationBag.cs create mode 100644 src/libraries/Highbyte.DotNet6502/Instrumentation/Instrumentations.cs rename src/{apps/Highbyte.DotNet6502.App.WASM => libraries/Highbyte.DotNet6502}/Instrumentation/Stats/AveragedStat.cs (61%) rename src/{apps/Highbyte.DotNet6502.App.WASM => libraries/Highbyte.DotNet6502}/Instrumentation/Stats/DisposableCallback.cs (81%) rename src/{apps/Highbyte.DotNet6502.App.WASM => libraries/Highbyte.DotNet6502}/Instrumentation/Stats/ElapsedMillisecondsStat.cs (80%) rename src/{apps/Highbyte.DotNet6502.App.SilkNetNative => libraries/Highbyte.DotNet6502}/Instrumentation/Stats/ElapsedMillisecondsTimedStat.cs (72%) rename src/{apps/Highbyte.DotNet6502.App.WASM => libraries/Highbyte.DotNet6502}/Instrumentation/Stats/IStat.cs (72%) rename src/{apps/Highbyte.DotNet6502.App.WASM => libraries/Highbyte.DotNet6502}/Instrumentation/Stats/PerSecondTimedStat.cs (75%) diff --git a/src/apps/Highbyte.DotNet6502.App.ConsoleMonitor/Program.cs b/src/apps/Highbyte.DotNet6502.App.ConsoleMonitor/Program.cs index 278c557d..d9fa3518 100644 --- a/src/apps/Highbyte.DotNet6502.App.ConsoleMonitor/Program.cs +++ b/src/apps/Highbyte.DotNet6502.App.ConsoleMonitor/Program.cs @@ -73,7 +73,7 @@ } else { - lastExecEvaluatorTriggerResult = systemRunner.RunEmulatorOneFrame(out _); + lastExecEvaluatorTriggerResult = systemRunner.RunEmulatorOneFrame(); if (lastExecEvaluatorTriggerResult.Triggered) { startMonitor = true; diff --git a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/Instrumentation/InstrumentationBag.cs b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/Instrumentation/InstrumentationBag.cs deleted file mode 100644 index 16bbf811..00000000 --- a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/Instrumentation/InstrumentationBag.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Highbyte.DotNet6502.App.SilkNetNative.Instrumentation.Stats; - -namespace Highbyte.DotNet6502.App.SilkNetNative.Stats; - -// Credit to instrumentation/stat code to: https://github.com/davidwengier/Trains.NET -public static class InstrumentationBag -{ - private static readonly List<(string Name, IStat Stat)> s_stats = new(); - public static IEnumerable<(string Name, IStat Stat)> Stats => s_stats.AsReadOnly(); - public static T Add(string name, T stat) where T : IStat - { - s_stats.Add((name, stat)); - return stat; - } - public static T Add(string name) where T : IStat, new() => Add(name, new T()); - - public static void Remove(string name) => s_stats.RemoveAll(s => s.Name == name); -} diff --git a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/Instrumentation/Stats/AveragedStat.cs b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/Instrumentation/Stats/AveragedStat.cs deleted file mode 100644 index 5c3966ab..00000000 --- a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/Instrumentation/Stats/AveragedStat.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace Highbyte.DotNet6502.App.SilkNetNative.Instrumentation.Stats; - -// Credit to instrumentation/stat code to: https://github.com/davidwengier/Trains.NET -public abstract class AveragedStat : IStat -{ - public double? Value { get; private set; } - private readonly int _sampleCount; - public AveragedStat(int sampleCount) - { - _sampleCount = sampleCount; - } - protected void SetValue(double value) - { - if (this.Value == null) - { - this.Value = value; - } - else - { - this.Value = (this.Value * (_sampleCount - 1) + value) / _sampleCount; - } - } - public abstract string GetDescription(); - - public bool ShouldShow() => this.Value.HasValue; -} diff --git a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/Instrumentation/Stats/DisposableCallback.cs b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/Instrumentation/Stats/DisposableCallback.cs deleted file mode 100644 index e887b776..00000000 --- a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/Instrumentation/Stats/DisposableCallback.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Highbyte.DotNet6502.App.SilkNetNative.Instrumentation.Stats; - -// Credit to instrumentation/stat code to: https://github.com/davidwengier/Trains.NET -public class DisposableCallback : IDisposable -{ - public event EventHandler? Disposing; - public void Dispose() - { - Disposing?.Invoke(this, EventArgs.Empty); - } -} \ No newline at end of file diff --git a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/Instrumentation/Stats/ElapsedMillisecondsStat.cs b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/Instrumentation/Stats/ElapsedMillisecondsStat.cs deleted file mode 100644 index 86de5e4f..00000000 --- a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/Instrumentation/Stats/ElapsedMillisecondsStat.cs +++ /dev/null @@ -1,35 +0,0 @@ -namespace Highbyte.DotNet6502.App.SilkNetNative.Instrumentation.Stats; - -public class ElapsedMillisecondsStat : AveragedStat -{ - private double _currentMs; - public ElapsedMillisecondsStat() - : base(10) // Average over 10 samples - { - _currentMs = 0; - } - public void Add(double ms) => _currentMs += ms; - public void Set(double ms) => _currentMs = ms; - public void UpdateStat() - { - SetValue(_currentMs); - _currentMs = 0; - } - - public override string GetDescription() - { - if (this.Value == null) - { - return "null"; - } - //double ms = Value.Value / TimeSpan.TicksPerMillisecond; // 10000 ticks per millisecond - double ms = Value.Value; - - if (ms < 0.01) - { - return "< 0.01ms"; - } - return Math.Round(ms, 2).ToString("0.00") + "ms"; - - } -} diff --git a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/Instrumentation/Stats/IStat.cs b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/Instrumentation/Stats/IStat.cs deleted file mode 100644 index 904606e0..00000000 --- a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/Instrumentation/Stats/IStat.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Highbyte.DotNet6502.App.SilkNetNative.Instrumentation.Stats; - -// Credit to instrumentation/stat code to: https://github.com/davidwengier/Trains.NET -public interface IStat -{ - string GetDescription(); - - bool ShouldShow(); -} \ No newline at end of file diff --git a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/Instrumentation/Stats/PerSecondTimedStat.cs b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/Instrumentation/Stats/PerSecondTimedStat.cs deleted file mode 100644 index 2baa6df4..00000000 --- a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/Instrumentation/Stats/PerSecondTimedStat.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System.Diagnostics; - -namespace Highbyte.DotNet6502.App.SilkNetNative.Instrumentation.Stats; - -// Credit to instrumentation/stat code to: https://github.com/davidwengier/Trains.NET -public class PerSecondTimedStat : AveragedStat -{ - private readonly Stopwatch _sw; - public PerSecondTimedStat() - : base(60) // Average over 60 samples - { - _sw = new Stopwatch(); - } - - public void Update() - { - if (_sw.IsRunning) - { - //var elapsedMs = _sw.ElapsedMilliseconds; - var elapsedMs = _sw.Elapsed.TotalMilliseconds; -#if DEBUG - if (elapsedMs == 0) - throw new NotImplementedException("Elapsed 0.0 milliseconds, cannot handle division by 0"); -#endif - double perSecond = 1000.0 / elapsedMs; - SetValue(perSecond); - } - _sw.Restart(); - } - - public override string GetDescription() - { - if (this.Value == null) - { - return "null"; - } - if (this.Value < 0.01) - { - return "< 0.01"; - } - return Math.Round(this.Value ?? 0, 2).ToString(); - } -} diff --git a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetImGuiStatsPanel.cs b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetImGuiStatsPanel.cs index d7b4ec35..926eaee8 100644 --- a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetImGuiStatsPanel.cs +++ b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetImGuiStatsPanel.cs @@ -1,6 +1,5 @@ using System.Numerics; -using Highbyte.DotNet6502.App.SilkNetNative.Instrumentation.Stats; -using Highbyte.DotNet6502.App.SilkNetNative.Stats; +using Highbyte.DotNet6502.Instrumentation.Stats; namespace Highbyte.DotNet6502.App.SilkNetNative; @@ -11,12 +10,15 @@ public class SilkNetImGuiStatsPanel : ISilkNetImGuiWindow private const int POS_X = 600; private const int POS_Y = 2; - private const int WIDTH = 400; + private const int WIDTH = 500; private const int HEIGHT = 300; static Vector4 s_LabelColor = new Vector4(0.7f, 0.7f, 0.7f, 1.0f); - public SilkNetImGuiStatsPanel() + private readonly Func> _getStats; + + public SilkNetImGuiStatsPanel(Func> getStats) { + _getStats = getStats; } public void PostOnRender() @@ -28,7 +30,7 @@ public void PostOnRender() //ImGui.SetWindowSize(new Vector2(WIDTH, HEIGHT)); var strings = new List(); - foreach ((string name, IStat stat) in InstrumentationBag.Stats.OrderBy(i => i.Name)) + foreach ((string name, IStat stat) in _getStats().OrderBy(i => i.Name)) { if (stat.ShouldShow()) { diff --git a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetWindow.cs b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetWindow.cs index a4fc35e5..86300113 100644 --- a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetWindow.cs +++ b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetWindow.cs @@ -1,12 +1,10 @@ -using System.Collections.Generic; using AutoMapper; -using Highbyte.DotNet6502.App.SilkNetNative; -using Highbyte.DotNet6502.App.SilkNetNative.Instrumentation.Stats; -using Highbyte.DotNet6502.App.SilkNetNative.Stats; using Highbyte.DotNet6502.Impl.NAudio; using Highbyte.DotNet6502.Impl.NAudio.NAudioOpenALProvider; using Highbyte.DotNet6502.Impl.SilkNet; using Highbyte.DotNet6502.Impl.Skia; +using Highbyte.DotNet6502.Instrumentation; +using Highbyte.DotNet6502.Instrumentation.Stats; using Highbyte.DotNet6502.Logging; using Highbyte.DotNet6502.Monitor; using Highbyte.DotNet6502.Systems; @@ -54,16 +52,19 @@ public float CanvasScale public EmulatorState EmulatorState { get; set; } = EmulatorState.Uninitialized; - private readonly ElapsedMillisecondsTimedStat _inputTime = InstrumentationBag.Add("SilkNet-InputTime"); - private readonly ElapsedMillisecondsTimedStat _systemTime = InstrumentationBag.Add("Emulator-SystemTime"); - private readonly ElapsedMillisecondsStat _systemTimeAudio = InstrumentationBag.Add("Emulator-SystemTime-Audio"); // Detailed part of system time - private readonly ElapsedMillisecondsTimedStat _renderTime = InstrumentationBag.Add("SilkNet-RenderTime"); - private readonly PerSecondTimedStat _updateFps = InstrumentationBag.Add("SilkNet-OnUpdateFPS"); - private readonly PerSecondTimedStat _renderFps = InstrumentationBag.Add("SilkNet-OnRenderFPS"); + private const string HostStatRootName = "SilkNet"; + private const string SystemTimeStatName = "Emulator-SystemTime"; + private const string RenderTimeStatName = "RenderTime"; + private const string InputTimeStatName = "InputTime"; + private const string AudioTimeStatName = "AudioTime"; + private readonly ElapsedMillisecondsTimedStat _systemTime = InstrumentationBag.Add($"{HostStatRootName}-{SystemTimeStatName}"); + private readonly ElapsedMillisecondsTimedStat _renderTime = InstrumentationBag.Add($"{HostStatRootName}-{RenderTimeStatName}"); + private readonly ElapsedMillisecondsTimedStat _inputTime = InstrumentationBag.Add($"{HostStatRootName}-{InputTimeStatName}"); + //private readonly ElapsedMillisecondsTimedStat _audioTime = InstrumentationBag.Add($"{HostStatRootName}-AudioTimeStatName}"); + private readonly PerSecondTimedStat _updateFps = InstrumentationBag.Add($"{HostStatRootName}-OnUpdateFPS"); + private readonly PerSecondTimedStat _renderFps = InstrumentationBag.Add($"{HostStatRootName}-OnRenderFPS"); + - private const string CustomSystemStatNamePrefix = "Emulator-SystemTime-Custom-"; - private const string CustomRenderStatNamePrefix = "SilkNet-RenderTime-Custom-"; - private Dictionary _customStats = new(); // Render context container for SkipSharp (surface/canvas) and SilkNetOpenGl private SilkNetRenderContextContainer _silkNetRenderContextContainer; @@ -249,33 +250,6 @@ public void SetCurrentSystem(string systemName) } } - private void InitCustomSystemStats() - { - // Remove any existing custom system stats - foreach (var existingCustomStatName in _customStats.Keys) - { - if (existingCustomStatName.StartsWith(CustomSystemStatNamePrefix) - || existingCustomStatName.StartsWith(CustomRenderStatNamePrefix)) - { - InstrumentationBag.Remove(existingCustomStatName); - _customStats.Remove(existingCustomStatName); - } - } - // Add any custom system stats for selected system - var system = _systemRunner.System; - foreach (var customStatName in system.DetailedStatNames) - { - _customStats.Add($"{CustomSystemStatNamePrefix}{customStatName}", InstrumentationBag.Add($"{CustomSystemStatNamePrefix}{customStatName}")); - } - - // Add any custom system stats for selected renderer - var renderer = _systemRunner.Renderer; - foreach (var customStatName in renderer.DetailedStatNames) - { - _customStats.Add($"{CustomRenderStatNamePrefix}{customStatName}", InstrumentationBag.Add($"{CustomRenderStatNamePrefix}{customStatName}")); - } - } - public void SetVolumePercent(float volumePercent) { _defaultAudioVolumePercent = volumePercent; @@ -294,8 +268,6 @@ public void Start() if (EmulatorState == EmulatorState.Uninitialized) _systemRunner = _systemList.BuildSystemRunner(_currentSystemName).Result; - InitCustomSystemStats(); - _monitor.Init(_systemRunner!); _systemRunner!.AudioHandler.StartPlaying(); @@ -364,25 +336,7 @@ private void RunEmulator() ExecEvaluatorTriggerResult execEvaluatorTriggerResult; using (_systemTime.Measure()) { - execEvaluatorTriggerResult = _systemRunner.RunEmulatorOneFrame(out var detailedStats); - - if (detailedStats.ContainsKey("Audio")) - { - _systemTimeAudio.Set(detailedStats["Audio"]); - _systemTimeAudio.UpdateStat(); - } - - // Update custom system stats - // TODO: Make custom system stats less messy? - foreach (var detailedStatName in detailedStats.Keys) - { - var statLookup = _customStats.Keys.SingleOrDefault(x => x.EndsWith(detailedStatName)); - if (statLookup != null) - { - _customStats[$"{CustomSystemStatNamePrefix}{detailedStatName}"].Set(detailedStats[detailedStatName]); - _customStats[$"{CustomSystemStatNamePrefix}{detailedStatName}"].UpdateStat(); - } - } + execEvaluatorTriggerResult = _systemRunner.RunEmulatorOneFrame(); } // Show monitor if we encounter breakpoint or other break @@ -419,28 +373,14 @@ private void RenderEmulator(double deltaTime) using (_renderTime.Measure()) { // Render emulator system screen - _systemRunner!.Draw(out var detailedStats); + _systemRunner!.Draw(); // Flush the SkiaSharp Context _silkNetRenderContextContainer.SkiaRenderContext.GetGRContext().Flush(); - - // Update custom system stats - // TODO: Make custom system stats less messy? - foreach (var detailedStatName in detailedStats.Keys) - { - var statLookup = _customStats.Keys.SingleOrDefault(x => x.EndsWith(detailedStatName)); - if (statLookup != null) - { - _customStats[$"{CustomRenderStatNamePrefix}{detailedStatName}"].Set(detailedStats[detailedStatName]); - _customStats[$"{CustomRenderStatNamePrefix}{detailedStatName}"].UpdateStat(); - } - } - } emulatorRendered = true; - // SilkNet windows are what's known as "double-buffered". In essence, the window manages two buffers. // One is rendered to while the other is currently displayed by the window. // This avoids screen tearing, a visual artifact that can happen if the buffer is modified while being displayed. @@ -545,7 +485,17 @@ private SilkNetImGuiMonitor CreateMonitorUI(SilkNetImGuiStatsPanel statsPanel, M private SilkNetImGuiStatsPanel CreateStatsUI() { - return new SilkNetImGuiStatsPanel(); + return new SilkNetImGuiStatsPanel(GetStats); + } + + private List<(string name, IStat stat)> GetStats() + { + return InstrumentationBag.Stats + .Union(_systemRunner.System.Stats.Stats.Select(x => (Name: $"{HostStatRootName}-{SystemTimeStatName}-{x.Name}", x.Stat))) + .Union(_systemRunner.Renderer.Stats.Stats.Select(x => (Name: $"{HostStatRootName}-{RenderTimeStatName}-{x.Name}", x.Stat))) + .Union(_systemRunner.AudioHandler.Stats.Stats.Select(x => (Name: $"{HostStatRootName}-{AudioTimeStatName}-{x.Name}", x.Stat))) + .Union(_systemRunner.InputHandler.Stats.Stats.Select(x => (Name: $"{HostStatRootName}-{InputTimeStatName}-{x.Name}", x.Stat))) + .ToList(); } private SilkNetImGuiLogsPanel CreateLogsUI(DotNet6502InMemLogStore logStore, DotNet6502InMemLoggerConfiguration logConfig) diff --git a/src/apps/Highbyte.DotNet6502.App.WASM/Instrumentation/InstrumentationBag.cs b/src/apps/Highbyte.DotNet6502.App.WASM/Instrumentation/InstrumentationBag.cs deleted file mode 100644 index 40eb3998..00000000 --- a/src/apps/Highbyte.DotNet6502.App.WASM/Instrumentation/InstrumentationBag.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Highbyte.DotNet6502.App.WASM.Instrumentation.Stats; - -namespace Highbyte.DotNet6502.App.WASM; - -// Credit to instrumentation/stat code to: https://github.com/davidwengier/Trains.NET -public static class InstrumentationBag -{ - private static readonly List<(string Name, IStat Stat)> s_stats = new(); - public static IEnumerable<(string Name, IStat Stat)> Stats => s_stats.AsReadOnly(); - public static T Add(string name, T stat) where T : IStat - { - s_stats.Add((name, stat)); - return stat; - } - public static T Add(string name) where T : IStat, new() => Add(name, new T()); - public static void Clear() => s_stats.Clear(); - - public static void Remove(string name) => s_stats.RemoveAll(s => s.Name == name); -} diff --git a/src/apps/Highbyte.DotNet6502.App.WASM/Instrumentation/Stats/ElapsedMillisecondsTimedStat.cs b/src/apps/Highbyte.DotNet6502.App.WASM/Instrumentation/Stats/ElapsedMillisecondsTimedStat.cs deleted file mode 100644 index 4044a23d..00000000 --- a/src/apps/Highbyte.DotNet6502.App.WASM/Instrumentation/Stats/ElapsedMillisecondsTimedStat.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System.Diagnostics; - -namespace Highbyte.DotNet6502.App.WASM.Instrumentation.Stats; - -// Credit to instrumentation/stat code to: https://github.com/davidwengier/Trains.NET -[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1001:Types that own disposable fields should be disposable", Justification = "Dispose is used to measure")] -public class ElapsedMillisecondsTimedStat : AveragedStat -{ - private readonly Stopwatch _sw; - private readonly DisposableCallback _disposableCallback; - public ElapsedMillisecondsTimedStat() - : base(10) // Average over 10 samples - { - _sw = new Stopwatch(); - _disposableCallback = new DisposableCallback(); - _disposableCallback.Disposing += (o, e) => Stop(); - } - public void Start() => _sw.Restart(); - public void Stop() - { - _sw.Stop(); - SetValue(_sw.ElapsedMilliseconds); - } - public IDisposable Measure() - { - Start(); - return _disposableCallback; - } - public override string GetDescription() - { - if (this.Value == null) - { - return "null"; - } - if (this.Value < 0.01) - { - return "< 0.01ms"; - } - return Math.Round(this.Value ?? 0, 2).ToString("0.00") + "ms"; - } -} diff --git a/src/apps/Highbyte.DotNet6502.App.WASM/Skia/WasmHost.cs b/src/apps/Highbyte.DotNet6502.App.WASM/Skia/WasmHost.cs index e398ef68..0f0a725d 100644 --- a/src/apps/Highbyte.DotNet6502.App.WASM/Skia/WasmHost.cs +++ b/src/apps/Highbyte.DotNet6502.App.WASM/Skia/WasmHost.cs @@ -1,7 +1,8 @@ -using Highbyte.DotNet6502.App.WASM.Instrumentation.Stats; using Highbyte.DotNet6502.Impl.AspNet; using Highbyte.DotNet6502.Impl.AspNet.JSInterop.BlazorWebAudioSync; using Highbyte.DotNet6502.Impl.Skia; +using Highbyte.DotNet6502.Instrumentation; +using Highbyte.DotNet6502.Instrumentation.Stats; using Highbyte.DotNet6502.Systems; using Toolbelt.Blazor.Gamepad; @@ -39,18 +40,17 @@ public class WasmHost : IDisposable public WasmMonitor Monitor { get; private set; } - private readonly ElapsedMillisecondsTimedStat _inputTime; + private const string HostStatRootName = "WASM"; + private const string SystemTimeStatName = "Emulator-SystemTime"; + private const string RenderTimeStatName = "RenderTime"; + private const string InputTimeStatName = "InputTime"; + private const string AudioTimeStatName = "AudioTime"; private readonly ElapsedMillisecondsTimedStat _systemTime; - private readonly ElapsedMillisecondsStat _systemTimeAudio; // Part of systemTime, but we want to show it separately private readonly ElapsedMillisecondsTimedStat _renderTime; + private readonly ElapsedMillisecondsTimedStat _inputTime; private readonly PerSecondTimedStat _updateFps; private readonly PerSecondTimedStat _renderFps; - private const string CustomSystemStatNamePrefix = "Emulator-SystemTime-Custom-"; - private const string CustomRenderStatNamePrefix = "WASMSkiaSharp-RenderTime-Custom-"; - private Dictionary _customStats = new(); - - private const int STATS_EVERY_X_FRAME = 60 * 1; private int _statsFrameCount = 0; @@ -84,14 +84,12 @@ public WasmHost( // Init stats InstrumentationBag.Clear(); - _inputTime = InstrumentationBag.Add("WASM-InputTime"); - - _systemTime = InstrumentationBag.Add("Emulator-SystemTime"); - _systemTimeAudio = InstrumentationBag.Add("Emulator-SystemTime-Audio"); - - _renderTime = InstrumentationBag.Add("WASMSkiaSharp-RenderTime"); - _updateFps = InstrumentationBag.Add("WASMSkiaSharp-OnUpdateFPS"); - _renderFps = InstrumentationBag.Add("WASMSkiaSharp-OnRenderFPS"); + _systemTime = InstrumentationBag.Add($"{HostStatRootName}-{SystemTimeStatName}"); + _inputTime = InstrumentationBag.Add($"{HostStatRootName}-{InputTimeStatName}"); + //_audioTime = InstrumentationBag.Add($"{HostStatRootName}-{AudioTimeStatName}"); + _renderTime = InstrumentationBag.Add($"{HostStatRootName}-{RenderTimeStatName}"); + _updateFps = InstrumentationBag.Add($"{HostStatRootName}-OnUpdateFPS"); + _renderFps = InstrumentationBag.Add($"{HostStatRootName}-OnRenderFPS"); Initialized = false; } @@ -111,8 +109,6 @@ public async Task Init(SKCanvas canvas, GRContext grContext, AudioContextSync au Monitor = new WasmMonitor(_jsRuntime, _systemRunner, _emulatorConfig, _setMonitorState); - InitCustomSystemStats(); - Initialized = true; } @@ -143,33 +139,6 @@ public void Start() _updateTimer!.Start(); } - private void InitCustomSystemStats() - { - // Remove any existing custom system stats - foreach (var existingCustomStatName in _customStats.Keys) - { - if (existingCustomStatName.StartsWith(CustomSystemStatNamePrefix) - || existingCustomStatName.StartsWith(CustomRenderStatNamePrefix)) - { - InstrumentationBag.Remove(existingCustomStatName); - _customStats.Remove(existingCustomStatName); - } - } - // Add any custom system stats for selected system - var system = _systemRunner.System; - foreach (var customStatName in system.DetailedStatNames) - { - _customStats.Add($"{CustomSystemStatNamePrefix}{customStatName}", InstrumentationBag.Add($"{CustomSystemStatNamePrefix}{customStatName}")); - } - - // Add any custom system stats for selected renderer - var renderer = _systemRunner.Renderer; - foreach (var customStatName in renderer.DetailedStatNames) - { - _customStats.Add($"{CustomRenderStatNamePrefix}{customStatName}", InstrumentationBag.Add($"{CustomRenderStatNamePrefix}{customStatName}")); - } - } - public void Cleanup() { if (_updateTimer != null) @@ -224,25 +193,7 @@ private void EmulatorRunOneFrame() ExecEvaluatorTriggerResult execEvaluatorTriggerResult; using (_systemTime.Measure()) { - execEvaluatorTriggerResult = _systemRunner.RunEmulatorOneFrame(out Dictionary detailedStats); - - if (detailedStats.ContainsKey("Audio")) - { - _systemTimeAudio.Set(detailedStats["Audio"]); - _systemTimeAudio.UpdateStat(); - } - - // Update custom system stats - // TODO: Make custom system stats less messy? - foreach (var detailedStatName in detailedStats.Keys) - { - var statLookup = _customStats.Keys.SingleOrDefault(x => x.EndsWith(detailedStatName)); - if (statLookup != null) - { - _customStats[$"{CustomSystemStatNamePrefix}{detailedStatName}"].Set(detailedStats[detailedStatName]); - _customStats[$"{CustomSystemStatNamePrefix}{detailedStatName}"].UpdateStat(); - } - } + execEvaluatorTriggerResult = _systemRunner.RunEmulatorOneFrame(); } _statsFrameCount++; @@ -272,23 +223,11 @@ public void Render(SKCanvas canvas, GRContext grContext) _skCanvas.Scale((float)_emulatorConfig.CurrentDrawScale); using (_renderTime.Measure()) { - _systemRunner.Draw(out Dictionary detailedStats); + _systemRunner.Draw(); //using (new SKAutoCanvasRestore(skCanvas)) //{ // _systemRunner.Draw(skCanvas); //} - - // Update custom system stats - // TODO: Make custom system stats less messy? - foreach (var detailedStatName in detailedStats.Keys) - { - var statLookup = _customStats.Keys.SingleOrDefault(x => x.EndsWith(detailedStatName)); - if (statLookup != null) - { - _customStats[$"{CustomRenderStatNamePrefix}{detailedStatName}"].Set(detailedStats[detailedStatName]); - _customStats[$"{CustomRenderStatNamePrefix}{detailedStatName}"].UpdateStat(); - } - } } } @@ -306,7 +245,13 @@ private string GetStats() { string stats = ""; - foreach ((string name, IStat stat) in InstrumentationBag.Stats.OrderBy(i => i.Name)) + var allStats = InstrumentationBag.Stats + .Union(_systemRunner.System.Stats.Stats.Select(x => (Name: $"{HostStatRootName}-{SystemTimeStatName}-{x.Name}", x.Stat))) + .Union(_systemRunner.Renderer.Stats.Stats.Select(x => (Name: $"{HostStatRootName}-{RenderTimeStatName}-{x.Name}", x.Stat))) + .Union(_systemRunner.AudioHandler.Stats.Stats.Select(x => (Name: $"{HostStatRootName}-{AudioTimeStatName}-{x.Name}", x.Stat))) + .Union(_systemRunner.InputHandler.Stats.Stats.Select(x => (Name: $"{HostStatRootName}-{InputTimeStatName}-{x.Name}", x.Stat))) + .ToList(); + foreach ((string name, IStat stat) in allStats.OrderBy(i => i.Name)) { if (stat.ShouldShow()) { diff --git a/src/libraries/Highbyte.DotNet6502.Impl.AspNet/Commodore64/Audio/C64WASMAudioHandler.cs b/src/libraries/Highbyte.DotNet6502.Impl.AspNet/Commodore64/Audio/C64WASMAudioHandler.cs index 09436400..f097291e 100644 --- a/src/libraries/Highbyte.DotNet6502.Impl.AspNet/Commodore64/Audio/C64WASMAudioHandler.cs +++ b/src/libraries/Highbyte.DotNet6502.Impl.AspNet/Commodore64/Audio/C64WASMAudioHandler.cs @@ -3,6 +3,7 @@ using Highbyte.DotNet6502.Systems.Commodore64.Audio; using Highbyte.DotNet6502.Impl.AspNet.JSInterop.BlazorWebAudioSync; using Microsoft.Extensions.Logging; +using Highbyte.DotNet6502.Instrumentation; namespace Highbyte.DotNet6502.Impl.AspNet.Commodore64.Audio; @@ -39,20 +40,18 @@ public class C64WASMAudioHandler : IAudioHandler {3, new C64WASMVoiceContext(3) }, }; - private readonly List _stats = new(); - private readonly ILogger _logger; + public List GetStats() => new(); + + // Stats + public Instrumentations Stats { get; } = new(); + public C64WASMAudioHandler(ILoggerFactory loggerFactory) { _logger = loggerFactory.CreateLogger(typeof(C64WASMAudioHandler).Name); } - public List GetStats() - { - return _stats; - } - public void Init(C64 system, WASMAudioHandlerContext audioHandlerContext) { _audioHandlerContext = audioHandlerContext; diff --git a/src/libraries/Highbyte.DotNet6502.Impl.AspNet/Commodore64/Input/C64AspNetInputHandler.cs b/src/libraries/Highbyte.DotNet6502.Impl.AspNet/Commodore64/Input/C64AspNetInputHandler.cs index 30aadafc..dfae21eb 100644 --- a/src/libraries/Highbyte.DotNet6502.Impl.AspNet/Commodore64/Input/C64AspNetInputHandler.cs +++ b/src/libraries/Highbyte.DotNet6502.Impl.AspNet/Commodore64/Input/C64AspNetInputHandler.cs @@ -1,4 +1,5 @@ using System.Globalization; +using Highbyte.DotNet6502.Instrumentation; using Highbyte.DotNet6502.Systems; using Highbyte.DotNet6502.Systems.Commodore64; using Highbyte.DotNet6502.Systems.Commodore64.TimerAndPeripheral; @@ -13,6 +14,9 @@ public class C64AspNetInputHandler : IInputHandler(); diff --git a/src/libraries/Highbyte.DotNet6502.Impl.AspNet/Generic/Input/GenericComputerAspNetInputHandler.cs b/src/libraries/Highbyte.DotNet6502.Impl.AspNet/Generic/Input/GenericComputerAspNetInputHandler.cs index a7086641..44c7aa7a 100644 --- a/src/libraries/Highbyte.DotNet6502.Impl.AspNet/Generic/Input/GenericComputerAspNetInputHandler.cs +++ b/src/libraries/Highbyte.DotNet6502.Impl.AspNet/Generic/Input/GenericComputerAspNetInputHandler.cs @@ -1,3 +1,4 @@ +using Highbyte.DotNet6502.Instrumentation; using Highbyte.DotNet6502.Systems; using Highbyte.DotNet6502.Systems.Generic; using Highbyte.DotNet6502.Systems.Generic.Config; @@ -8,7 +9,11 @@ public class GenericComputerAspNetInputHandler : IInputHandler _stats = new(); + + public List GetStats() => new(); + + // Stats + public Instrumentations Stats { get; } = new(); public GenericComputerAspNetInputHandler(EmulatorInputConfig emulatorInputConfig) { @@ -57,9 +62,4 @@ private void CaptureKeyboard(GenericComputer genericComputer) // Only way to tell the "GenericComputer" that a Key is no longer pressed is to set KeyDownAddress to 0... genericComputer.Mem[_emulatorInputConfig.KeyDownAddress] = 0x00; } - - public List GetStats() - { - return _stats; - } } diff --git a/src/libraries/Highbyte.DotNet6502.Impl.NAudio/Commodore64/Audio/C64NAudioAudioHandler.cs b/src/libraries/Highbyte.DotNet6502.Impl.NAudio/Commodore64/Audio/C64NAudioAudioHandler.cs index 1256a42b..b3b362b1 100644 --- a/src/libraries/Highbyte.DotNet6502.Impl.NAudio/Commodore64/Audio/C64NAudioAudioHandler.cs +++ b/src/libraries/Highbyte.DotNet6502.Impl.NAudio/Commodore64/Audio/C64NAudioAudioHandler.cs @@ -1,3 +1,4 @@ +using Highbyte.DotNet6502.Instrumentation; using Highbyte.DotNet6502.Systems; using Highbyte.DotNet6502.Systems.Commodore64; using Highbyte.DotNet6502.Systems.Commodore64.Audio; @@ -31,8 +32,6 @@ public class C64NAudioAudioHandler : IAudioHandler _stats = new(); - private readonly ILogger _logger; public C64NAudioAudioHandler(ILoggerFactory loggerFactory) @@ -40,10 +39,11 @@ public C64NAudioAudioHandler(ILoggerFactory loggerFactory) _logger = loggerFactory.CreateLogger(typeof(C64NAudioAudioHandler).Name); } - public List GetStats() - { - return _stats; - } + public List GetStats() => new(); + + // Stats + public Instrumentations Stats { get; } = new(); + public void Init(C64 system, NAudioAudioHandlerContext audioHandlerContext) { diff --git a/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Commodore64/Input/C64SadConsoleInputHandler.cs b/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Commodore64/Input/C64SadConsoleInputHandler.cs index 9d5904c8..ffc05fe2 100644 --- a/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Commodore64/Input/C64SadConsoleInputHandler.cs +++ b/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Commodore64/Input/C64SadConsoleInputHandler.cs @@ -1,4 +1,5 @@ using Highbyte.DotNet6502; +using Highbyte.DotNet6502.Instrumentation; using Highbyte.DotNet6502.Systems; using Highbyte.DotNet6502.Systems.Commodore64; using Highbyte.DotNet6502.Systems.Commodore64.TimerAndPeripheral; @@ -13,6 +14,9 @@ public class C64SadConsoleInputHandler : IInputHandler _logger; + // Stats + public Instrumentations Stats { get; } = new(); + public C64SadConsoleInputHandler(ILoggerFactory loggerFactory) { _logger = loggerFactory.CreateLogger(); diff --git a/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Commodore64/Video/C64SadConsoleRenderer.cs b/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Commodore64/Video/C64SadConsoleRenderer.cs index a64e4402..0f2830aa 100644 --- a/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Commodore64/Video/C64SadConsoleRenderer.cs +++ b/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Commodore64/Video/C64SadConsoleRenderer.cs @@ -1,3 +1,4 @@ +using Highbyte.DotNet6502.Instrumentation; using Highbyte.DotNet6502.Systems; using Highbyte.DotNet6502.Systems.Commodore64; using Highbyte.DotNet6502.Systems.Commodore64.Video; @@ -9,8 +10,8 @@ public class C64SadConsoleRenderer : IRenderer private SadConsoleRenderContext _sadConsoleRenderContext = default!; private C64SadConsoleColors _c64SadConsoleColors = default!; - public bool HasDetailedStats => false; - public List DetailedStatNames => new List(); + // Stats + public Instrumentations Stats { get; } = new(); public C64SadConsoleRenderer() { @@ -27,15 +28,15 @@ public void Init(ISystem system, IRenderContext renderContext) Init((C64)system, (SadConsoleRenderContext)renderContext); } - public void Draw(C64 c64, Dictionary detailedStats) + public void Draw(C64 c64) { RenderMainScreen(c64); RenderBorder(c64); } - public void Draw(ISystem system, Dictionary detailedStats) + public void Draw(ISystem system) { - Draw((C64)system, detailedStats); + Draw((C64)system); } private void RenderMainScreen(C64 c64) diff --git a/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Generic/Input/GenericSadConsoleInputHandler.cs b/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Generic/Input/GenericSadConsoleInputHandler.cs index c7201bea..49223903 100644 --- a/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Generic/Input/GenericSadConsoleInputHandler.cs +++ b/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Generic/Input/GenericSadConsoleInputHandler.cs @@ -1,3 +1,4 @@ +using Highbyte.DotNet6502.Instrumentation; using Highbyte.DotNet6502.Systems; using Highbyte.DotNet6502.Systems.Generic; using Highbyte.DotNet6502.Systems.Generic.Config; @@ -11,7 +12,12 @@ public class GenericSadConsoleInputHandler : IInputHandler _stats = new(); + + public List GetStats() => new(); + + // Stats + public Instrumentations Stats { get; } = new(); + public GenericSadConsoleInputHandler(EmulatorInputConfig emulatorInputConfig, ILoggerFactory loggerFactory) { @@ -69,8 +75,4 @@ private void CaptureRandomNumber(GenericComputer system) var rnd = (byte)new Random().Next(0, 255); emulatorMem[_emulatorInputConfig.RandomValueAddress] = rnd; } - public List GetStats() - { - return _stats; - } } diff --git a/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Generic/Video/GenericSadConsoleRenderer.cs b/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Generic/Video/GenericSadConsoleRenderer.cs index 18ecce5a..072addac 100644 --- a/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Generic/Video/GenericSadConsoleRenderer.cs +++ b/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Generic/Video/GenericSadConsoleRenderer.cs @@ -1,3 +1,4 @@ +using Highbyte.DotNet6502.Instrumentation; using Highbyte.DotNet6502.Systems; using Highbyte.DotNet6502.Systems.Generic; using Highbyte.DotNet6502.Systems.Generic.Config; @@ -10,8 +11,7 @@ public class GenericSadConsoleRenderer : IRenderer false; - public List DetailedStatNames => new List(); + public Instrumentations Stats { get; } = new(); public GenericSadConsoleRenderer(EmulatorScreenConfig emulatorScreenConfig) { @@ -30,16 +30,16 @@ public void Init(ISystem system, IRenderContext renderContext) Init((GenericComputer)system, (SadConsoleRenderContext)renderContext); } - public void Draw(GenericComputer system, Dictionary detailedStats) + public void Draw(GenericComputer system) { RenderMainScreen(system); if (_emulatorScreenConfig.BorderCols > 0 || _emulatorScreenConfig.BorderRows > 0) RenderBorder(system); } - public void Draw(ISystem system, Dictionary detailedStats) + public void Draw(ISystem system) { - Draw((GenericComputer)system, detailedStats); + Draw((GenericComputer)system); } private void RenderMainScreen(GenericComputer system) diff --git a/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/SadConsoleMain.cs b/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/SadConsoleMain.cs index 7717b35c..f321cca7 100644 --- a/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/SadConsoleMain.cs +++ b/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/SadConsoleMain.cs @@ -87,10 +87,10 @@ private void UpdateSadConsole(object sender, GameHost e) _systemRunner.ProcessInput(); // Run CPU for one frame - var execEvaluatorTriggerResult = _systemRunner.RunEmulatorOneFrame(out _); + var execEvaluatorTriggerResult = _systemRunner.RunEmulatorOneFrame(); // Update SadConsole screen - _systemRunner.Draw(out Dictionary detailedStats); + _systemRunner.Draw(); if (execEvaluatorTriggerResult.Triggered) { diff --git a/src/libraries/Highbyte.DotNet6502.Impl.SilkNet/Commodore64/Input/C64SilkNetInputHandler.cs b/src/libraries/Highbyte.DotNet6502.Impl.SilkNet/Commodore64/Input/C64SilkNetInputHandler.cs index 314da6f8..bdb9f6e9 100644 --- a/src/libraries/Highbyte.DotNet6502.Impl.SilkNet/Commodore64/Input/C64SilkNetInputHandler.cs +++ b/src/libraries/Highbyte.DotNet6502.Impl.SilkNet/Commodore64/Input/C64SilkNetInputHandler.cs @@ -1,3 +1,4 @@ +using Highbyte.DotNet6502.Instrumentation; using Highbyte.DotNet6502.Systems; using Highbyte.DotNet6502.Systems.Commodore64; using Highbyte.DotNet6502.Systems.Commodore64.TimerAndPeripheral; @@ -8,12 +9,16 @@ namespace Highbyte.DotNet6502.Impl.SilkNet.Commodore64.Input; public class C64SilkNetInputHandler : IInputHandler { private SilkNetInputHandlerContext? _inputHandlerContext; - private readonly List _stats = new(); private readonly C64SilkNetKeyboard _c64SilkNetKeyboard; //private readonly C64SilkNetGamepad _c64SilkNetGamepad; private readonly ILogger _logger; private readonly C64SilkNetConfig _c64SilkNetConfig; + public List GetStats() => new(); + + // Stats + public Instrumentations Stats { get; } = new(); + public C64SilkNetInputHandler(ILoggerFactory loggerFactory, C64SilkNetConfig c64SilkNetConfig) { @@ -154,9 +159,4 @@ private HashSet GetC64JoystickActionsFromSilkNetGamepad(HashS } return c64JoystickActions; } - - public List GetStats() - { - return _stats; - } } diff --git a/src/libraries/Highbyte.DotNet6502.Impl.SilkNet/Commodore64/Video/C64SilkNetOpenGlRenderer.cs b/src/libraries/Highbyte.DotNet6502.Impl.SilkNet/Commodore64/Video/C64SilkNetOpenGlRenderer.cs index fa4cc1fb..eff86e61 100644 --- a/src/libraries/Highbyte.DotNet6502.Impl.SilkNet/Commodore64/Video/C64SilkNetOpenGlRenderer.cs +++ b/src/libraries/Highbyte.DotNet6502.Impl.SilkNet/Commodore64/Video/C64SilkNetOpenGlRenderer.cs @@ -1,5 +1,7 @@ using System.Numerics; using Highbyte.DotNet6502.Impl.SilkNet.OpenGLHelpers; +using Highbyte.DotNet6502.Instrumentation; +using Highbyte.DotNet6502.Instrumentation.Stats; using Highbyte.DotNet6502.Systems; using Highbyte.DotNet6502.Systems.Commodore64; using Highbyte.DotNet6502.Systems.Commodore64.Config; @@ -17,8 +19,7 @@ public class C64SilkNetOpenGlRenderer : IRenderer true; - public List DetailedStatNames => new List() { }; + public Instrumentations Stats { get; } = new(); // Types for Uniform Buffer Objects, must align to 16 bytes. public struct TextData @@ -191,13 +192,8 @@ public void Init(ISystem system, IRenderContext renderContext) Init((C64)system, (SilkNetOpenGlRenderContext)renderContext); } - public void Draw(C64 c64, Dictionary detailedStats) + public void Draw(C64 c64) { - foreach (var detailedStatName in DetailedStatNames) - { - detailedStats[detailedStatName] = 0; - } - var vic2 = c64.Vic2; var vic2Mem = vic2.Vic2Mem; var vic2Screen = vic2.Vic2Screen; @@ -330,9 +326,9 @@ public void Draw(C64 c64, Dictionary detailedStats) _gl.DrawArrays(GLEnum.Triangles, 0, 6); } - public void Draw(ISystem system, Dictionary detailedStats) + public void Draw(ISystem system) { - Draw((C64)system, detailedStats); + Draw((C64)system); } private TextData[] BuildTextScreenData(C64 c64) diff --git a/src/libraries/Highbyte.DotNet6502.Impl.SilkNet/Generic/Input/GenericComputerSilkNetInputHandler.cs b/src/libraries/Highbyte.DotNet6502.Impl.SilkNet/Generic/Input/GenericComputerSilkNetInputHandler.cs index cc6d5ae9..a9e0b958 100644 --- a/src/libraries/Highbyte.DotNet6502.Impl.SilkNet/Generic/Input/GenericComputerSilkNetInputHandler.cs +++ b/src/libraries/Highbyte.DotNet6502.Impl.SilkNet/Generic/Input/GenericComputerSilkNetInputHandler.cs @@ -1,3 +1,4 @@ +using Highbyte.DotNet6502.Instrumentation; using Highbyte.DotNet6502.Systems; using Highbyte.DotNet6502.Systems.Generic; using Highbyte.DotNet6502.Systems.Generic.Config; @@ -8,7 +9,10 @@ public class GenericComputerSilkNetInputHandler : IInputHandler _stats = new(); + public List GetStats() => new(); + + // Stats + public Instrumentations Stats { get; } = new(); public GenericComputerSilkNetInputHandler(EmulatorInputConfig emulatorInputConfig) { @@ -50,9 +54,4 @@ private void CaptureKeyboard(GenericComputer genericComputer) genericComputer.Mem[_emulatorInputConfig.KeyDownAddress] = 0x00; } } - - public List GetStats() - { - return _stats; - } } diff --git a/src/libraries/Highbyte.DotNet6502.Impl.Skia/Commodore64/Video/C64SkiaRenderer.cs b/src/libraries/Highbyte.DotNet6502.Impl.Skia/Commodore64/Video/C64SkiaRenderer.cs index b404ff0b..a335a02a 100644 --- a/src/libraries/Highbyte.DotNet6502.Impl.Skia/Commodore64/Video/C64SkiaRenderer.cs +++ b/src/libraries/Highbyte.DotNet6502.Impl.Skia/Commodore64/Video/C64SkiaRenderer.cs @@ -1,4 +1,5 @@ -using System.Diagnostics; +using Highbyte.DotNet6502.Instrumentation; +using Highbyte.DotNet6502.Instrumentation.Stats; using Highbyte.DotNet6502.Systems; using Highbyte.DotNet6502.Systems.Commodore64; using Highbyte.DotNet6502.Systems.Commodore64.Config; @@ -33,30 +34,23 @@ public class C64SkiaRenderer : IRenderer // Sprite drawing variables private readonly SKImage[] _spriteImages; - public bool HasDetailedStats => true; - public List DetailedStatNames => new List() - { - DrawBorderStatName, - DrawBackgroundStatName, - DrawTextScreenStatName, - DrawSpritesStatName, - }; - private const string DrawBorderStatName = "Border"; - private const string DrawBackgroundStatName = "Background"; - private const string DrawTextScreenStatName = "TextScreen"; - private const string DrawSpritesStatName = "Sprites"; - private Dictionary _detailedStatStopwatches = new() - { - {DrawBorderStatName, new Stopwatch() }, - {DrawBackgroundStatName, new Stopwatch() }, - {DrawTextScreenStatName, new Stopwatch() }, - {DrawSpritesStatName, new Stopwatch() } - }; + // Stats + public Instrumentations Stats { get; } = new(); + private const string StatsCategory = "SkiaSharp-Custom"; + private readonly ElapsedMillisecondsTimedStat _borderStat; + private readonly ElapsedMillisecondsTimedStat _backgroundStat; + private readonly ElapsedMillisecondsTimedStat _textScreenStat; + private readonly ElapsedMillisecondsTimedStat _spritesStat; public C64SkiaRenderer() { _charGen = new CharGen(); _spriteImages = new SKImage[Vic2SpriteManager.NUMBERS_OF_SPRITES]; + + _backgroundStat = Stats.Add($"{StatsCategory}-Background"); + _borderStat = Stats.Add($"{StatsCategory}-Border"); + _spritesStat = Stats.Add($"{StatsCategory}-Sprites"); + _textScreenStat = Stats.Add($"{StatsCategory}-TextScreen"); } public void Init(C64 c64, SkiaRenderContext skiaRenderContext) @@ -73,51 +67,42 @@ public void Init(ISystem system, IRenderContext renderContext) Init((C64)system, (SkiaRenderContext)renderContext); } - public void Draw(C64 c64, Dictionary detailedStats) + public void Draw(C64 c64) { - foreach (var detailedStatName in DetailedStatNames) + var canvas = _getSkCanvas(); + canvas.Clear(); + + using (_borderStat.Measure()) { - detailedStats[detailedStatName] = 0; + //DrawSimpleBorder(c64, canvas); + DrawRasterLinesBorder(c64, canvas); } - var canvas = _getSkCanvas(); - canvas.Clear(); + using (_backgroundStat.Measure()) + { + //DrawSimpleBackground(c64, canvas); + DrawRasterLinesBackground(c64, canvas); + } - var swBorder = _detailedStatStopwatches[DrawBorderStatName]; - swBorder.Restart(); - //DrawSimpleBorder(c64, canvas); - DrawRasterLinesBorder(c64, canvas); - swBorder.Stop(); - detailedStats[DrawBorderStatName] = swBorder.Elapsed.TotalMilliseconds; - - var swBg = _detailedStatStopwatches[DrawBackgroundStatName]; - swBg.Restart(); - //DrawSimpleBackground(c64, canvas); - DrawRasterLinesBackground(c64, canvas); - swBg.Stop(); - detailedStats[DrawBackgroundStatName] = swBg.Elapsed.TotalMilliseconds; - - var swSprites = _detailedStatStopwatches[DrawSpritesStatName]; - swSprites.Restart(); - RenderSprites(c64, canvas, spritesWithPriorityOverForeground: false); - swSprites.Stop(); - detailedStats[DrawSpritesStatName] = swBg.Elapsed.TotalMilliseconds; - - var swTextScreen = _detailedStatStopwatches[DrawTextScreenStatName]; - swTextScreen.Restart(); - RenderMainScreen(c64, canvas); - swTextScreen.Stop(); - detailedStats[DrawTextScreenStatName] = swTextScreen.Elapsed.TotalMilliseconds; - - swSprites.Restart(); - RenderSprites(c64, canvas, spritesWithPriorityOverForeground: true); - swSprites.Stop(); - detailedStats[DrawBackgroundStatName] += swSprites.Elapsed.TotalMilliseconds; + using (_spritesStat.Measure()) + { + RenderSprites(c64, canvas, spritesWithPriorityOverForeground: false); + } + + using (_textScreenStat.Measure()) + { + RenderMainScreen(c64, canvas); + } + + using (_spritesStat.Measure(cont: true)) + { + RenderSprites(c64, canvas, spritesWithPriorityOverForeground: true); + } } - public void Draw(ISystem system, Dictionary detailedStats) + public void Draw(ISystem system) { - Draw((C64)system, detailedStats); + Draw((C64)system); } private void InitCharset(C64 c64) diff --git a/src/libraries/Highbyte.DotNet6502.Impl.Skia/Generic/Video/GenericComputerSkiaRenderer.cs b/src/libraries/Highbyte.DotNet6502.Impl.Skia/Generic/Video/GenericComputerSkiaRenderer.cs index d1e2115f..1fa0aec0 100644 --- a/src/libraries/Highbyte.DotNet6502.Impl.Skia/Generic/Video/GenericComputerSkiaRenderer.cs +++ b/src/libraries/Highbyte.DotNet6502.Impl.Skia/Generic/Video/GenericComputerSkiaRenderer.cs @@ -1,4 +1,6 @@ using System.Reflection; +using Highbyte.DotNet6502.Instrumentation.Stats; +using Highbyte.DotNet6502.Instrumentation; using Highbyte.DotNet6502.Systems; using Highbyte.DotNet6502.Systems.Generic; using Highbyte.DotNet6502.Systems.Generic.Config; @@ -16,8 +18,7 @@ public class GenericComputerSkiaRenderer : IRenderer false; - public List DetailedStatNames => new List(); + public Instrumentations Stats { get; } = new(); public GenericComputerSkiaRenderer(EmulatorScreenConfig emulatorScreenConfig) { @@ -43,7 +44,7 @@ public void Init(ISystem system, IRenderContext renderContext) Init((GenericComputer)system, (SkiaRenderContext)renderContext); } - public void Draw(GenericComputer genericComputer, Dictionary detailedStats) + public void Draw(GenericComputer genericComputer) { var mem = genericComputer.Mem; var canvas = _getSkCanvas(); @@ -82,9 +83,9 @@ public void Draw(GenericComputer genericComputer, Dictionary det } } - public void Draw(ISystem system, Dictionary detailedStats) + public void Draw(ISystem system) { - Draw((GenericComputer)system, detailedStats); + Draw((GenericComputer)system); } private string GetDrawTextFromCharacter(byte chr) diff --git a/src/libraries/Highbyte.DotNet6502.Systems/Commodore64/C64.cs b/src/libraries/Highbyte.DotNet6502.Systems/Commodore64/C64.cs index 38859ba9..9425923d 100644 --- a/src/libraries/Highbyte.DotNet6502.Systems/Commodore64/C64.cs +++ b/src/libraries/Highbyte.DotNet6502.Systems/Commodore64/C64.cs @@ -1,4 +1,6 @@ using System.Diagnostics; +using Highbyte.DotNet6502.Instrumentation.Stats; +using Highbyte.DotNet6502.Instrumentation; using Highbyte.DotNet6502.Monitor.SystemSpecific; using Highbyte.DotNet6502.Systems.Commodore64.Audio; using Highbyte.DotNet6502.Systems.Commodore64.Config; @@ -41,17 +43,11 @@ public class C64 : ISystem, ISystemMonitor private readonly ILogger _logger; public const ushort BASIC_LOAD_ADDRESS = 0x0801; - // Detailed stats - public bool HasDetailedStats => true; - public List DetailedStatNames => new List() - { - SpriteCollisionStatName - }; - private const string SpriteCollisionStatName = "SpriteCollision"; - private Dictionary _detailedStatStopwatches = new() - { - {SpriteCollisionStatName, new Stopwatch() } - }; + // Stats + public Instrumentations Stats { get; } = new(); + private const string StatsCategory = "Custom"; + private readonly ElapsedMillisecondsTimedStat _spriteCollisionStat; + private readonly ElapsedMillisecondsTimedStat _audioStat; //public static ROM[] ROMS = new ROM[] //{ @@ -73,20 +69,16 @@ public class C64 : ISystem, ISystemMonitor /// public ExecEvaluatorTriggerResult ExecuteOneFrame( SystemRunner systemRunner, - Dictionary detailedStats, IExecEvaluator? execEvaluator = null) { - foreach (var detailedStatName in DetailedStatNames) - { - detailedStats[detailedStatName] = 0; - } + _audioStat.Reset(); // Reset audio stat, will be continiously updated after each instruction ulong cyclesToExecute = (Vic2.Vic2Model.CyclesPerFrame - Vic2.CyclesConsumedCurrentVblank); _logger.LogTrace($"Executing one frame, {cyclesToExecute} CPU cycles."); ulong totalCyclesConsumed = 0; while (totalCyclesConsumed < cyclesToExecute) { - ExecEvaluatorTriggerResult execEvaluatorTriggerResult = ExecuteOneInstruction(systemRunner, out InstructionExecResult instructionExecResult, detailedStats, execEvaluator); + ExecEvaluatorTriggerResult execEvaluatorTriggerResult = ExecuteOneInstruction(systemRunner, out InstructionExecResult instructionExecResult, execEvaluator); totalCyclesConsumed += instructionExecResult.CyclesConsumed; if (execEvaluatorTriggerResult.Triggered) @@ -96,11 +88,10 @@ public ExecEvaluatorTriggerResult ExecuteOneFrame( } // Update sprite collision state - var sw = _detailedStatStopwatches[SpriteCollisionStatName]; - sw.Restart(); - Vic2.SpriteManager.SetCollitionDetectionStatesAndIRQ(); - sw.Stop(); - detailedStats[SpriteCollisionStatName] += sw.Elapsed.TotalMilliseconds; + using (_spriteCollisionStat.Measure()) + { + Vic2.SpriteManager.SetCollitionDetectionStatesAndIRQ(); + } return ExecEvaluatorTriggerResult.NotTriggered; } @@ -116,7 +107,6 @@ public ExecEvaluatorTriggerResult ExecuteOneFrame( public ExecEvaluatorTriggerResult ExecuteOneInstruction( SystemRunner systemRunner, out InstructionExecResult instructionExecResult, - Dictionary detailedStats, IExecEvaluator? execEvaluator = null) { // Execute one CPU instruction @@ -131,7 +121,12 @@ public ExecEvaluatorTriggerResult ExecuteOneInstruction( // Handle output processing needed after each instruction. if (AudioEnabled) - systemRunner.GenerateAudio(detailedStats); + { + using (_audioStat.Measure(cont: true)) + { + systemRunner.GenerateAudio(); + } + } // Check for debugger breakpoints (or other possible IExecEvaluator implementations used). if (execEvaluator != null) @@ -148,6 +143,8 @@ public ExecEvaluatorTriggerResult ExecuteOneInstruction( private C64(ILogger logger) { _logger = logger; + _spriteCollisionStat = Stats.Add($"{StatsCategory}-SpriteCollision"); + _audioStat = Stats.Add($"{StatsCategory}-Audio"); } public static C64 BuildC64(C64Config c64Config, ILoggerFactory loggerFactory) diff --git a/src/libraries/Highbyte.DotNet6502.Systems/Generic/GenericComputer.cs b/src/libraries/Highbyte.DotNet6502.Systems/Generic/GenericComputer.cs index 6fe060b7..03c23452 100644 --- a/src/libraries/Highbyte.DotNet6502.Systems/Generic/GenericComputer.cs +++ b/src/libraries/Highbyte.DotNet6502.Systems/Generic/GenericComputer.cs @@ -1,3 +1,4 @@ +using Highbyte.DotNet6502.Instrumentation; using Highbyte.DotNet6502.Systems.Generic.Config; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; @@ -36,13 +37,14 @@ public class GenericComputer : ISystem, ITextMode, IScreen public int VisibleTopBottomBorderHeight => (VisibleHeight - DrawableAreaHeight) / 2; public float RefreshFrequencyHz => _genericComputerConfig.ScreenRefreshFrequencyHz; - public bool HasDetailedStats => false; - public List DetailedStatNames => new(); - private ILogger _logger; private GenericComputerConfig _genericComputerConfig; private readonly LegacyExecEvaluator _oneFrameExecEvaluator; + // Stats + public Instrumentations Stats { get; } = new(); + + public GenericComputer() : this(new GenericComputerConfig(), new NullLoggerFactory()) { } public GenericComputer(GenericComputerConfig genericComputerConfig, ILoggerFactory loggerFactory) { @@ -71,7 +73,6 @@ public void Run(IExecEvaluator? execEvaluator = null) public ExecEvaluatorTriggerResult ExecuteOneFrame( SystemRunner systemRunner, - Dictionary detailedStats, IExecEvaluator? execEvaluator = null) { // If we already executed cycles in current frame, reduce it from total. @@ -110,7 +111,7 @@ public ExecEvaluatorTriggerResult ExecuteOneFrame( SetFrameCompleted(); // Wait for CPU 6502 code has acknowledged that it knows a frame has completed. - bool waitOk = WaitFrameCompletedAcknowledged(systemRunner, detailedStats); + bool waitOk = WaitFrameCompletedAcknowledged(systemRunner); if (!waitOk) return ExecEvaluatorTriggerResult.CreateTrigger(ExecEvaluatorTriggerReasonType.Other, "WaitFrame failed"); ; } @@ -122,7 +123,6 @@ public ExecEvaluatorTriggerResult ExecuteOneFrame( public ExecEvaluatorTriggerResult ExecuteOneInstruction( SystemRunner systemRunner, out InstructionExecResult instructionExecResult, - Dictionary detailedStats, IExecEvaluator? execEvaluator = null) { var execState = CPU.ExecuteOneInstruction(Mem); @@ -147,12 +147,12 @@ private void SetFrameCompleted() Mem.SetBit(_genericComputerConfig.Memory.Screen.ScreenRefreshStatusAddress, (int)ScreenStatusBitFlags.HostNewFrame); } - private bool WaitFrameCompletedAcknowledged(SystemRunner systemRunner, Dictionary detailedStats) + private bool WaitFrameCompletedAcknowledged(SystemRunner systemRunner) { // Keep on executing instructions until CPU 6502 code has cleared bit 0 in ScreenRefreshStatusAddress while (Mem.IsBitSet(_genericComputerConfig.Memory.Screen.ScreenRefreshStatusAddress, (int)ScreenStatusBitFlags.HostNewFrame)) { - var execEvaluatorTriggerResult = ExecuteOneInstruction(systemRunner, out _, detailedStats); + var execEvaluatorTriggerResult = ExecuteOneInstruction(systemRunner, out _); // If an unhandled instruction or other configured trigger has activated, return false if (execEvaluatorTriggerResult.Triggered) return false; diff --git a/src/libraries/Highbyte.DotNet6502/Instrumentation/InstrumentationBag.cs b/src/libraries/Highbyte.DotNet6502/Instrumentation/InstrumentationBag.cs new file mode 100644 index 00000000..2fb4d895 --- /dev/null +++ b/src/libraries/Highbyte.DotNet6502/Instrumentation/InstrumentationBag.cs @@ -0,0 +1,18 @@ +using Highbyte.DotNet6502.Instrumentation.Stats; + +namespace Highbyte.DotNet6502.Instrumentation; + +// Credit for basis of instrumentation/stat code to: https://github.com/davidwengier/Trains.NET +public static class InstrumentationBag +{ + private static readonly Instrumentations s_instrumentations = new(); + public static IEnumerable<(string Name, IStat Stat)> Stats => s_instrumentations.Stats; + public static T Add(string name, T stat) where T : IStat + { + s_instrumentations.Add(name, stat); + return stat; + } + public static T Add(string name) where T : IStat, new() => Add(name, new T()); + public static void Remove(string name) => s_instrumentations.Remove(name); + public static void Clear() => s_instrumentations.Clear(); +} diff --git a/src/libraries/Highbyte.DotNet6502/Instrumentation/Instrumentations.cs b/src/libraries/Highbyte.DotNet6502/Instrumentation/Instrumentations.cs new file mode 100644 index 00000000..4e05b230 --- /dev/null +++ b/src/libraries/Highbyte.DotNet6502/Instrumentation/Instrumentations.cs @@ -0,0 +1,17 @@ +using Highbyte.DotNet6502.Instrumentation.Stats; + +namespace Highbyte.DotNet6502.Instrumentation; + +public class Instrumentations +{ + private readonly List<(string Name, IStat Stat)> _stats = new(); + public IEnumerable<(string Name, IStat Stat)> Stats => _stats.AsReadOnly(); + public T Add(string name, T stat) where T : IStat + { + _stats.Add((name, stat)); + return stat; + } + public T Add(string name) where T : IStat, new() => Add(name, new T()); + public void Remove(string name) => _stats.RemoveAll(s => s.Name == name); + public void Clear() => _stats.Clear(); +} diff --git a/src/apps/Highbyte.DotNet6502.App.WASM/Instrumentation/Stats/AveragedStat.cs b/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/AveragedStat.cs similarity index 61% rename from src/apps/Highbyte.DotNet6502.App.WASM/Instrumentation/Stats/AveragedStat.cs rename to src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/AveragedStat.cs index ceee03a5..7d320592 100644 --- a/src/apps/Highbyte.DotNet6502.App.WASM/Instrumentation/Stats/AveragedStat.cs +++ b/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/AveragedStat.cs @@ -1,4 +1,4 @@ -namespace Highbyte.DotNet6502.App.WASM.Instrumentation.Stats; +namespace Highbyte.DotNet6502.Instrumentation.Stats; // Credit to instrumentation/stat code to: https://github.com/davidwengier/Trains.NET public abstract class AveragedStat : IStat @@ -11,16 +11,14 @@ public AveragedStat(int sampleCount) } protected void SetValue(double value) { - if (this.Value == null) - { - this.Value = value; - } + if (Value == null) + Value = value; else { - this.Value = (this.Value * (_sampleCount - 1) + value) / _sampleCount; + Value = (Value * (_sampleCount - 1) + value) / _sampleCount; } } public abstract string GetDescription(); - public bool ShouldShow() => this.Value.HasValue; + public bool ShouldShow() => Value.HasValue; } diff --git a/src/apps/Highbyte.DotNet6502.App.WASM/Instrumentation/Stats/DisposableCallback.cs b/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/DisposableCallback.cs similarity index 81% rename from src/apps/Highbyte.DotNet6502.App.WASM/Instrumentation/Stats/DisposableCallback.cs rename to src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/DisposableCallback.cs index c06b0787..70415f99 100644 --- a/src/apps/Highbyte.DotNet6502.App.WASM/Instrumentation/Stats/DisposableCallback.cs +++ b/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/DisposableCallback.cs @@ -1,4 +1,4 @@ -namespace Highbyte.DotNet6502.App.WASM.Instrumentation.Stats; +namespace Highbyte.DotNet6502.Instrumentation.Stats; // Credit to instrumentation/stat code to: https://github.com/davidwengier/Trains.NET public class DisposableCallback : IDisposable diff --git a/src/apps/Highbyte.DotNet6502.App.WASM/Instrumentation/Stats/ElapsedMillisecondsStat.cs b/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/ElapsedMillisecondsStat.cs similarity index 80% rename from src/apps/Highbyte.DotNet6502.App.WASM/Instrumentation/Stats/ElapsedMillisecondsStat.cs rename to src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/ElapsedMillisecondsStat.cs index b5e5f617..faf5111b 100644 --- a/src/apps/Highbyte.DotNet6502.App.WASM/Instrumentation/Stats/ElapsedMillisecondsStat.cs +++ b/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/ElapsedMillisecondsStat.cs @@ -1,4 +1,4 @@ -namespace Highbyte.DotNet6502.App.WASM.Instrumentation.Stats; +namespace Highbyte.DotNet6502.Instrumentation.Stats; public class ElapsedMillisecondsStat : AveragedStat { @@ -18,17 +18,13 @@ public void UpdateStat() public override string GetDescription() { - if (this.Value == null) - { + if (Value == null) return "null"; - } //double ms = Value.Value / TimeSpan.TicksPerMillisecond; // 10000 ticks per millisecond - double ms = Value.Value; + var ms = Value.Value; if (ms < 0.01) - { return "< 0.01ms"; - } return Math.Round(ms, 2).ToString("0.00") + "ms"; } diff --git a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/Instrumentation/Stats/ElapsedMillisecondsTimedStat.cs b/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/ElapsedMillisecondsTimedStat.cs similarity index 72% rename from src/apps/Highbyte.DotNet6502.App.SilkNetNative/Instrumentation/Stats/ElapsedMillisecondsTimedStat.cs rename to src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/ElapsedMillisecondsTimedStat.cs index ca5f622a..5ac6ca23 100644 --- a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/Instrumentation/Stats/ElapsedMillisecondsTimedStat.cs +++ b/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/ElapsedMillisecondsTimedStat.cs @@ -1,6 +1,6 @@ using System.Diagnostics; -namespace Highbyte.DotNet6502.App.SilkNetNative.Instrumentation.Stats; +namespace Highbyte.DotNet6502.Instrumentation.Stats; // Credit to instrumentation/stat code to: https://github.com/davidwengier/Trains.NET [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1001:Types that own disposable fields should be disposable", Justification = "Dispose is used to measure")] @@ -15,32 +15,41 @@ public ElapsedMillisecondsTimedStat() _disposableCallback = new DisposableCallback(); _disposableCallback.Disposing += (o, e) => Stop(); } - public void Start() => _sw.Restart(); + + public void Reset() + { + _sw.Reset(); + } + + public void Start(bool cont = false) + { + if (cont) + _sw.Start(); + else + _sw.Restart(); + } + public void Stop() { _sw.Stop(); //SetValue(_sw.ElapsedMilliseconds); SetValue(_sw.Elapsed.Ticks); } - public IDisposable Measure() + public IDisposable Measure(bool cont = false) { - Start(); + Start(cont); return _disposableCallback; } public override string GetDescription() { - if (this.Value == null) - { + if (Value == null) return "null"; - } //double ms = Value.Value / 10000.0d; // 10000 ticks per millisecond - double ms = Value.Value / TimeSpan.TicksPerMillisecond; // 10000 ticks per millisecond + var ms = Value.Value / TimeSpan.TicksPerMillisecond; // 10000 ticks per millisecond if (ms < 0.01) - { return "< 0.01ms"; - } return Math.Round(ms, 2).ToString("0.00") + "ms"; } } diff --git a/src/apps/Highbyte.DotNet6502.App.WASM/Instrumentation/Stats/IStat.cs b/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/IStat.cs similarity index 72% rename from src/apps/Highbyte.DotNet6502.App.WASM/Instrumentation/Stats/IStat.cs rename to src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/IStat.cs index efdf2ebb..0bb0d864 100644 --- a/src/apps/Highbyte.DotNet6502.App.WASM/Instrumentation/Stats/IStat.cs +++ b/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/IStat.cs @@ -1,4 +1,4 @@ -namespace Highbyte.DotNet6502.App.WASM.Instrumentation.Stats; +namespace Highbyte.DotNet6502.Instrumentation.Stats; // Credit to instrumentation/stat code to: https://github.com/davidwengier/Trains.NET public interface IStat diff --git a/src/apps/Highbyte.DotNet6502.App.WASM/Instrumentation/Stats/PerSecondTimedStat.cs b/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/PerSecondTimedStat.cs similarity index 75% rename from src/apps/Highbyte.DotNet6502.App.WASM/Instrumentation/Stats/PerSecondTimedStat.cs rename to src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/PerSecondTimedStat.cs index 6b55ca14..96ab07b9 100644 --- a/src/apps/Highbyte.DotNet6502.App.WASM/Instrumentation/Stats/PerSecondTimedStat.cs +++ b/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/PerSecondTimedStat.cs @@ -1,6 +1,6 @@ using System.Diagnostics; -namespace Highbyte.DotNet6502.App.WASM.Instrumentation.Stats; +namespace Highbyte.DotNet6502.Instrumentation.Stats; // Credit to instrumentation/stat code to: https://github.com/davidwengier/Trains.NET public class PerSecondTimedStat : AveragedStat @@ -22,7 +22,7 @@ public void Update() if (elapsedMs == 0) throw new NotImplementedException("Elapsed 0.0 milliseconds, cannot handle division by 0"); #endif - double perSecond = 1000.0 / elapsedMs; + var perSecond = 1000.0 / elapsedMs; SetValue(perSecond); } _sw.Restart(); @@ -30,14 +30,10 @@ public void Update() public override string GetDescription() { - if (this.Value == null) - { + if (Value == null) return "null"; - } - if (this.Value < 0.01) - { + if (Value < 0.01) return "< 0.01"; - } - return Math.Round(this.Value ?? 0, 2).ToString(); + return Math.Round(Value ?? 0, 2).ToString(); } } diff --git a/src/libraries/Highbyte.DotNet6502/Systems/IAudioHandler.cs b/src/libraries/Highbyte.DotNet6502/Systems/IAudioHandler.cs index bdc06543..d72e61f5 100644 --- a/src/libraries/Highbyte.DotNet6502/Systems/IAudioHandler.cs +++ b/src/libraries/Highbyte.DotNet6502/Systems/IAudioHandler.cs @@ -1,3 +1,5 @@ +using Highbyte.DotNet6502.Instrumentation; + namespace Highbyte.DotNet6502.Systems; public interface IAudioHandler @@ -8,9 +10,8 @@ public interface IAudioHandler void StartPlaying(); void StopPlaying(); void PausePlaying(); - List GetStats(); - + Instrumentations Stats { get; } } public interface IAudioHandler : IAudioHandler @@ -48,8 +49,7 @@ public void StopPlaying() { } - private readonly List _stats = new List(); - - public List GetStats() => _stats; + public List GetStats() => new(); + public Instrumentations Stats { get; } = new(); } diff --git a/src/libraries/Highbyte.DotNet6502/Systems/IInputHandler.cs b/src/libraries/Highbyte.DotNet6502/Systems/IInputHandler.cs index 9b9f045c..5e18f59c 100644 --- a/src/libraries/Highbyte.DotNet6502/Systems/IInputHandler.cs +++ b/src/libraries/Highbyte.DotNet6502/Systems/IInputHandler.cs @@ -1,3 +1,5 @@ +using Highbyte.DotNet6502.Instrumentation; + namespace Highbyte.DotNet6502.Systems; public interface IInputHandler @@ -6,6 +8,7 @@ public interface IInputHandler void ProcessInput(ISystem system); List GetStats(); + Instrumentations Stats { get; } } public interface IInputHandler : IInputHandler @@ -26,7 +29,7 @@ public void Init(ISystem system, IInputHandlerContext inputHandlerContext) public void ProcessInput(ISystem system) { } + public List GetStats() => new(); - private readonly List _stats = new List(); - public List GetStats() => _stats; + public Instrumentations Stats { get; } = new(); } diff --git a/src/libraries/Highbyte.DotNet6502/Systems/IRenderer.cs b/src/libraries/Highbyte.DotNet6502/Systems/IRenderer.cs index 37d6191b..611f6de1 100644 --- a/src/libraries/Highbyte.DotNet6502/Systems/IRenderer.cs +++ b/src/libraries/Highbyte.DotNet6502/Systems/IRenderer.cs @@ -1,13 +1,13 @@ +using Highbyte.DotNet6502.Instrumentation; + namespace Highbyte.DotNet6502.Systems; public interface IRenderer { void Init(ISystem system, IRenderContext renderContext); - void Draw(ISystem system, Dictionary detailedStats); - - public bool HasDetailedStats { get; } - public List DetailedStatNames { get; } + void Draw(ISystem system); + Instrumentations Stats { get; } } public interface IRenderer : IRenderer @@ -15,19 +15,18 @@ public interface IRenderer : IRenderer where TRenderContext : IRenderContext { void Init(TSystem system, TRenderContext renderContext); - void Draw(TSystem system, Dictionary detailedStats); + void Draw(TSystem system); } public class NullRenderer : IRenderer { - public bool HasDetailedStats => false; - public List DetailedStatNames => new List(); + public Instrumentations Stats { get; } = new(); public void Init(ISystem system, IRenderContext renderContext) { } - public void Draw(ISystem system, Dictionary detailedStats) + public void Draw(ISystem system) { } } diff --git a/src/libraries/Highbyte.DotNet6502/Systems/ISystem.cs b/src/libraries/Highbyte.DotNet6502/Systems/ISystem.cs index b303e0ab..b0423974 100644 --- a/src/libraries/Highbyte.DotNet6502/Systems/ISystem.cs +++ b/src/libraries/Highbyte.DotNet6502/Systems/ISystem.cs @@ -1,3 +1,5 @@ +using Highbyte.DotNet6502.Instrumentation; + namespace Highbyte.DotNet6502.Systems; public interface ISystem @@ -9,17 +11,14 @@ public interface ISystem Memory Mem { get; } IScreen Screen { get; } - public ExecEvaluatorTriggerResult ExecuteOneFrame( + ExecEvaluatorTriggerResult ExecuteOneFrame( SystemRunner systemRunner, - Dictionary detailedStats, IExecEvaluator? execEvaluator = null); - public ExecEvaluatorTriggerResult ExecuteOneInstruction( + ExecEvaluatorTriggerResult ExecuteOneInstruction( SystemRunner systemRunner, out InstructionExecResult instructionExecResult, - Dictionary detailedStats, IExecEvaluator? execEvaluator = null); - public bool HasDetailedStats { get; } - public List DetailedStatNames { get; } + Instrumentations Stats { get; } } diff --git a/src/libraries/Highbyte.DotNet6502/Systems/SystemRunner.cs b/src/libraries/Highbyte.DotNet6502/Systems/SystemRunner.cs index dd7bb3c7..f54a13d8 100644 --- a/src/libraries/Highbyte.DotNet6502/Systems/SystemRunner.cs +++ b/src/libraries/Highbyte.DotNet6502/Systems/SystemRunner.cs @@ -53,44 +53,29 @@ public void ProcessInput() /// Called by host app by a timer (or similar) that runs the emulator, tied to the update frequency of the emulated system. /// Typically called before after ProcessInput is called. /// - public ExecEvaluatorTriggerResult RunEmulatorOneFrame(out Dictionary detailedStats) + public ExecEvaluatorTriggerResult RunEmulatorOneFrame() { - detailedStats = new() - { - ["Audio"] = 0 - }; - - var execEvaluatorTriggerResult = _system.ExecuteOneFrame(this, detailedStats, _customExecEvaluator); + var execEvaluatorTriggerResult = _system.ExecuteOneFrame(this, _customExecEvaluator); return execEvaluatorTriggerResult; } /// /// Called by host app that runs the emulator, typically once per frame tied to the host app rendering frequency. /// - public void Draw(out Dictionary detailedStats) + public void Draw() { - detailedStats = new() - { - }; - - _renderer?.Draw(_system, detailedStats); + _renderer?.Draw(_system); } /// /// Called by the specific ISystem implementation after each instruction or entire frame worth of instructions, depending how audio is implemented. /// /// - public void GenerateAudio(Dictionary detailedStats) + public void GenerateAudio() { - if (_audioHandler is not null) - { - _audioSw.Restart(); - _audioHandler.GenerateAudio(_system); - //var t = new Task(() => _audioHandler?.GenerateAudio(system)); - //t.RunSynchronously(); - _audioSw.Stop(); + _audioHandler?.GenerateAudio(_system); - detailedStats["Audio"] += _audioSw.Elapsed.TotalMilliseconds; - } + //var t = new Task(() => _audioHandler?.GenerateAudio(system)); + //t.RunSynchronously(); } } diff --git a/tests/Highbyte.DotNet6502.Tests/Systems/SystemRunnerBuilderTests.cs b/tests/Highbyte.DotNet6502.Tests/Systems/SystemRunnerBuilderTests.cs index 8c13c188..c22060fb 100644 --- a/tests/Highbyte.DotNet6502.Tests/Systems/SystemRunnerBuilderTests.cs +++ b/tests/Highbyte.DotNet6502.Tests/Systems/SystemRunnerBuilderTests.cs @@ -1,3 +1,4 @@ +using Highbyte.DotNet6502.Instrumentation; using Highbyte.DotNet6502.Systems; namespace Highbyte.DotNet6502.Tests.Systems; @@ -112,16 +113,14 @@ public class TestSystem : ISystem public IScreen Screen => throw new NotImplementedException(); - public bool HasDetailedStats => false; + public Instrumentations Stats { get; } = new(); - public List DetailedStatNames => new List(); - - public ExecEvaluatorTriggerResult ExecuteOneFrame(SystemRunner systemRunner, Dictionary detailedStats, IExecEvaluator? execEvaluator = null) + public ExecEvaluatorTriggerResult ExecuteOneFrame(SystemRunner systemRunner, IExecEvaluator? execEvaluator = null) { return new ExecEvaluatorTriggerResult(); } - public ExecEvaluatorTriggerResult ExecuteOneInstruction(SystemRunner systemRunner, out InstructionExecResult instructionExecResult, Dictionary detailedStats, IExecEvaluator? execEvaluator = null) + public ExecEvaluatorTriggerResult ExecuteOneInstruction(SystemRunner systemRunner, out InstructionExecResult instructionExecResult, IExecEvaluator? execEvaluator = null) { instructionExecResult = new InstructionExecResult(); return new ExecEvaluatorTriggerResult(); @@ -130,10 +129,10 @@ public ExecEvaluatorTriggerResult ExecuteOneInstruction(SystemRunner systemRunne public class TestRenderer : IRenderer { - public void Draw(TestSystem system, Dictionary detailedStats) + public void Draw(TestSystem system) { } - public void Draw(ISystem system, Dictionary detailedStats) + public void Draw(ISystem system) { } public void Init(TestSystem system, IRenderContext renderContext) @@ -142,29 +141,21 @@ public void Init(TestSystem system, IRenderContext renderContext) public void Init(ISystem system, IRenderContext renderContext) { } - - public bool HasDetailedStats => false; - public List DetailedStatNames => new List(); + public Instrumentations Stats { get; } = new(); } public class TestRendererNonGeneric : IRenderer { - public void Draw(ISystem system, Dictionary detailedStats) + public void Draw(ISystem system) { } public void Init(ISystem system, IRenderContext renderContext) { } - - public bool HasDetailedStats => false; - public List DetailedStatNames => new List(); + public Instrumentations Stats { get; } = new(); } public class TestInputHandler : IInputHandler { - public List GetStats() - { - return new List(); - } public void Init(TestSystem system, IInputHandlerContext inputContext) { } @@ -177,6 +168,10 @@ public void ProcessInput(TestSystem system) public void ProcessInput(ISystem system) { } + + public List GetStats() => new(); + + public Instrumentations Stats { get; } = new(); } public class TestInputHandlerNonGeneric : IInputHandler @@ -185,14 +180,13 @@ public List GetStats() { return new List(); } - public void Init(ISystem system, IInputHandlerContext inputContext) { } - public void ProcessInput(ISystem system) { } + public Instrumentations Stats { get; } = new(); } public class TestAudioHandler : IAudioHandler @@ -203,10 +197,6 @@ public void GenerateAudio(TestSystem system) public void GenerateAudio(ISystem system) { } - public List GetStats() - { - return new List(); - } public void Init(TestSystem system, IAudioHandlerContext audioHandlerContext) { } @@ -222,6 +212,10 @@ public void StartPlaying() public void StopPlaying() { } + public List GetStats() => new(); + + public Instrumentations Stats { get; } = new(); + } public class TestAudioHandlerNonGeneric : IAudioHandler @@ -229,10 +223,6 @@ public class TestAudioHandlerNonGeneric : IAudioHandler public void GenerateAudio(ISystem system) { } - public List GetStats() - { - return new List(); - } public void Init(ISystem system, IAudioHandlerContext audioHandlerContext) { } @@ -245,4 +235,8 @@ public void StartPlaying() public void StopPlaying() { } + public List GetStats() => new(); + + public Instrumentations Stats { get; } = new(); + } From 161db6d8101ad151da847790ef9bb4c367828336 Mon Sep 17 00:00:00 2001 From: Highbyte Date: Tue, 28 Nov 2023 21:25:08 +0100 Subject: [PATCH 02/10] Rename old code that was names "stats" but was actually debug info. --- .../Highbyte.DotNet6502.App.WASM/Skia/WasmHost.cs | 10 +++++----- .../Commodore64/Audio/C64WASMAudioHandler.cs | 11 +---------- .../Commodore64/Input/C64AspNetInputHandler.cs | 2 +- .../Input/GenericComputerAspNetInputHandler.cs | 2 +- .../Commodore64/Audio/C64NAudioAudioHandler.cs | 2 +- .../Commodore64/Input/C64SadConsoleInputHandler.cs | 6 +++--- .../Generic/Input/GenericSadConsoleInputHandler.cs | 2 +- .../Commodore64/Input/C64SilkNetInputHandler.cs | 2 +- .../Input/GenericComputerSilkNetInputHandler.cs | 2 +- .../Highbyte.DotNet6502/Systems/IAudioHandler.cs | 4 ++-- .../Highbyte.DotNet6502/Systems/IInputHandler.cs | 4 ++-- .../Systems/SystemRunnerBuilderTests.cs | 12 +++++------- 12 files changed, 24 insertions(+), 35 deletions(-) diff --git a/src/apps/Highbyte.DotNet6502.App.WASM/Skia/WasmHost.cs b/src/apps/Highbyte.DotNet6502.App.WASM/Skia/WasmHost.cs index 0f0a725d..34ce71cc 100644 --- a/src/apps/Highbyte.DotNet6502.App.WASM/Skia/WasmHost.cs +++ b/src/apps/Highbyte.DotNet6502.App.WASM/Skia/WasmHost.cs @@ -267,18 +267,18 @@ private string GetDebugMessages() { string debugMessages = ""; - var inputStats = _systemRunner.InputHandler.GetStats(); - var inputStatsOneString = string.Join(" # ", inputStats); + var inputDebugInfo = _systemRunner.InputHandler.GetDebugInfo(); + var inputStatsOneString = string.Join(" # ", inputDebugInfo); debugMessages += $"{BuildHtmlString("INPUT", "header")}: {BuildHtmlString(inputStatsOneString, "value")} "; - //foreach (var message in inputStats) + //foreach (var message in inputDebugInfo) //{ // if (debugMessages != "") // debugMessages += "
"; // debugMessages += $"{BuildHtmlString("DEBUG INPUT", "header")}: {BuildHtmlString(message, "value")} "; //} - var audioStats = _systemRunner.AudioHandler.GetStats(); - foreach (var message in audioStats) + var audioDebugInfo = _systemRunner.AudioHandler.GetDebugInfo(); + foreach (var message in audioDebugInfo) { if (debugMessages != "") debugMessages += "
"; diff --git a/src/libraries/Highbyte.DotNet6502.Impl.AspNet/Commodore64/Audio/C64WASMAudioHandler.cs b/src/libraries/Highbyte.DotNet6502.Impl.AspNet/Commodore64/Audio/C64WASMAudioHandler.cs index f097291e..3abbab8b 100644 --- a/src/libraries/Highbyte.DotNet6502.Impl.AspNet/Commodore64/Audio/C64WASMAudioHandler.cs +++ b/src/libraries/Highbyte.DotNet6502.Impl.AspNet/Commodore64/Audio/C64WASMAudioHandler.cs @@ -42,7 +42,7 @@ public class C64WASMAudioHandler : IAudioHandler private readonly ILogger _logger; - public List GetStats() => new(); + public List GetDebugInfo() => new(); // Stats public Instrumentations Stats { get; } = new(); @@ -270,15 +270,6 @@ private void AddDebugMessage(string msg, int? voice = null, SidVoiceWaveForm? si } _logger.LogDebug(formattedMsg); - - //var time = DateTime.Now.ToString("HH:mm:ss.fff"); - //formattedMsg = $"{time}: {formattedMsg}"; - ////var threadId = Environment.CurrentManagedThreadId; - ////_stats.Insert(0, $"{time} ({threadId}): {msg}"); - //_stats.Insert(0, formattedMsg); - - //if (_stats.Count > MAX_DEBUG_MESSAGES) - // _stats.RemoveAt(MAX_DEBUG_MESSAGES); } //private Task[] CreateSoundTasks(InternalSidState sidInternalStateClone) diff --git a/src/libraries/Highbyte.DotNet6502.Impl.AspNet/Commodore64/Input/C64AspNetInputHandler.cs b/src/libraries/Highbyte.DotNet6502.Impl.AspNet/Commodore64/Input/C64AspNetInputHandler.cs index dfae21eb..81fc6495 100644 --- a/src/libraries/Highbyte.DotNet6502.Impl.AspNet/Commodore64/Input/C64AspNetInputHandler.cs +++ b/src/libraries/Highbyte.DotNet6502.Impl.AspNet/Commodore64/Input/C64AspNetInputHandler.cs @@ -156,7 +156,7 @@ private HashSet GetC64JoystickActionsFromAspNetGamepad(HashSe } - public List GetStats() + public List GetDebugInfo() { List list = new(); if (_inputHandlerContext == null) diff --git a/src/libraries/Highbyte.DotNet6502.Impl.AspNet/Generic/Input/GenericComputerAspNetInputHandler.cs b/src/libraries/Highbyte.DotNet6502.Impl.AspNet/Generic/Input/GenericComputerAspNetInputHandler.cs index 44c7aa7a..f9306f0f 100644 --- a/src/libraries/Highbyte.DotNet6502.Impl.AspNet/Generic/Input/GenericComputerAspNetInputHandler.cs +++ b/src/libraries/Highbyte.DotNet6502.Impl.AspNet/Generic/Input/GenericComputerAspNetInputHandler.cs @@ -10,7 +10,7 @@ public class GenericComputerAspNetInputHandler : IInputHandler GetStats() => new(); + public List GetDebugInfo() => new(); // Stats public Instrumentations Stats { get; } = new(); diff --git a/src/libraries/Highbyte.DotNet6502.Impl.NAudio/Commodore64/Audio/C64NAudioAudioHandler.cs b/src/libraries/Highbyte.DotNet6502.Impl.NAudio/Commodore64/Audio/C64NAudioAudioHandler.cs index b3b362b1..0ec90e2c 100644 --- a/src/libraries/Highbyte.DotNet6502.Impl.NAudio/Commodore64/Audio/C64NAudioAudioHandler.cs +++ b/src/libraries/Highbyte.DotNet6502.Impl.NAudio/Commodore64/Audio/C64NAudioAudioHandler.cs @@ -39,7 +39,7 @@ public C64NAudioAudioHandler(ILoggerFactory loggerFactory) _logger = loggerFactory.CreateLogger(typeof(C64NAudioAudioHandler).Name); } - public List GetStats() => new(); + public List GetDebugInfo() => new(); // Stats public Instrumentations Stats { get; } = new(); diff --git a/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Commodore64/Input/C64SadConsoleInputHandler.cs b/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Commodore64/Input/C64SadConsoleInputHandler.cs index ffc05fe2..6b8b9952 100644 --- a/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Commodore64/Input/C64SadConsoleInputHandler.cs +++ b/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Commodore64/Input/C64SadConsoleInputHandler.cs @@ -10,7 +10,7 @@ namespace Highbyte.DotNet6502.Impl.SadConsole.Commodore64.Input; public class C64SadConsoleInputHandler : IInputHandler { private SadConsoleInputHandlerContext? _inputHandlerContext; - private readonly List _stats = new(); + private readonly List _debugInfo = new(); private readonly C64SadConsoleKeyboard _c64SadConsoleKeyboard; private readonly ILogger _logger; @@ -103,8 +103,8 @@ private List GetC64KeysFromSadConsoleKeys(List keysDown, out bool return c64KeysDown; } - public List GetStats() + public List GetDebugInfo() { - return _stats; + return _debugInfo; } } diff --git a/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Generic/Input/GenericSadConsoleInputHandler.cs b/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Generic/Input/GenericSadConsoleInputHandler.cs index 49223903..25ecdf89 100644 --- a/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Generic/Input/GenericSadConsoleInputHandler.cs +++ b/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Generic/Input/GenericSadConsoleInputHandler.cs @@ -13,7 +13,7 @@ public class GenericSadConsoleInputHandler : IInputHandler GetStats() => new(); + public List GetDebugInfo() => new(); // Stats public Instrumentations Stats { get; } = new(); diff --git a/src/libraries/Highbyte.DotNet6502.Impl.SilkNet/Commodore64/Input/C64SilkNetInputHandler.cs b/src/libraries/Highbyte.DotNet6502.Impl.SilkNet/Commodore64/Input/C64SilkNetInputHandler.cs index bdb9f6e9..b084e6a5 100644 --- a/src/libraries/Highbyte.DotNet6502.Impl.SilkNet/Commodore64/Input/C64SilkNetInputHandler.cs +++ b/src/libraries/Highbyte.DotNet6502.Impl.SilkNet/Commodore64/Input/C64SilkNetInputHandler.cs @@ -14,7 +14,7 @@ public class C64SilkNetInputHandler : IInputHandler _logger; private readonly C64SilkNetConfig _c64SilkNetConfig; - public List GetStats() => new(); + public List GetDebugInfo() => new(); // Stats public Instrumentations Stats { get; } = new(); diff --git a/src/libraries/Highbyte.DotNet6502.Impl.SilkNet/Generic/Input/GenericComputerSilkNetInputHandler.cs b/src/libraries/Highbyte.DotNet6502.Impl.SilkNet/Generic/Input/GenericComputerSilkNetInputHandler.cs index a9e0b958..72bd6940 100644 --- a/src/libraries/Highbyte.DotNet6502.Impl.SilkNet/Generic/Input/GenericComputerSilkNetInputHandler.cs +++ b/src/libraries/Highbyte.DotNet6502.Impl.SilkNet/Generic/Input/GenericComputerSilkNetInputHandler.cs @@ -9,7 +9,7 @@ public class GenericComputerSilkNetInputHandler : IInputHandler GetStats() => new(); + public List GetDebugInfo() => new(); // Stats public Instrumentations Stats { get; } = new(); diff --git a/src/libraries/Highbyte.DotNet6502/Systems/IAudioHandler.cs b/src/libraries/Highbyte.DotNet6502/Systems/IAudioHandler.cs index d72e61f5..352c698b 100644 --- a/src/libraries/Highbyte.DotNet6502/Systems/IAudioHandler.cs +++ b/src/libraries/Highbyte.DotNet6502/Systems/IAudioHandler.cs @@ -10,7 +10,7 @@ public interface IAudioHandler void StartPlaying(); void StopPlaying(); void PausePlaying(); - List GetStats(); + List GetDebugInfo(); Instrumentations Stats { get; } } @@ -49,7 +49,7 @@ public void StopPlaying() { } - public List GetStats() => new(); + public List GetDebugInfo() => new(); public Instrumentations Stats { get; } = new(); } diff --git a/src/libraries/Highbyte.DotNet6502/Systems/IInputHandler.cs b/src/libraries/Highbyte.DotNet6502/Systems/IInputHandler.cs index 5e18f59c..03b0bdbb 100644 --- a/src/libraries/Highbyte.DotNet6502/Systems/IInputHandler.cs +++ b/src/libraries/Highbyte.DotNet6502/Systems/IInputHandler.cs @@ -7,7 +7,7 @@ public interface IInputHandler void Init(ISystem system, IInputHandlerContext inputContext); void ProcessInput(ISystem system); - List GetStats(); + List GetDebugInfo(); Instrumentations Stats { get; } } @@ -29,7 +29,7 @@ public void Init(ISystem system, IInputHandlerContext inputHandlerContext) public void ProcessInput(ISystem system) { } - public List GetStats() => new(); + public List GetDebugInfo() => new(); public Instrumentations Stats { get; } = new(); } diff --git a/tests/Highbyte.DotNet6502.Tests/Systems/SystemRunnerBuilderTests.cs b/tests/Highbyte.DotNet6502.Tests/Systems/SystemRunnerBuilderTests.cs index c22060fb..88a6812c 100644 --- a/tests/Highbyte.DotNet6502.Tests/Systems/SystemRunnerBuilderTests.cs +++ b/tests/Highbyte.DotNet6502.Tests/Systems/SystemRunnerBuilderTests.cs @@ -169,23 +169,21 @@ public void ProcessInput(ISystem system) { } - public List GetStats() => new(); + public List GetDebugInfo() => new(); public Instrumentations Stats { get; } = new(); } public class TestInputHandlerNonGeneric : IInputHandler { - public List GetStats() - { - return new List(); - } + public void Init(ISystem system, IInputHandlerContext inputContext) { } public void ProcessInput(ISystem system) { } + public List GetDebugInfo() => new List(); public Instrumentations Stats { get; } = new(); } @@ -212,7 +210,7 @@ public void StartPlaying() public void StopPlaying() { } - public List GetStats() => new(); + public List GetDebugInfo() => new(); public Instrumentations Stats { get; } = new(); @@ -235,7 +233,7 @@ public void StartPlaying() public void StopPlaying() { } - public List GetStats() => new(); + public List GetDebugInfo() => new(); public Instrumentations Stats { get; } = new(); From 95805b5fb3488cd96a2d664a094ccf5165676044 Mon Sep 17 00:00:00 2001 From: Highbyte Date: Tue, 28 Nov 2023 21:29:13 +0100 Subject: [PATCH 03/10] Rename new Stats members to Instrumentations --- .../SilkNetImGuiMenu.cs | 2 +- .../SilkNetImGuiStatsPanel.cs | 2 +- .../SilkNetWindow.cs | 14 ++++++-------- .../Pages/Index.razor.cs | 2 +- .../Highbyte.DotNet6502.App.WASM/Skia/WasmHost.cs | 8 ++++---- .../Commodore64/Audio/C64WASMAudioHandler.cs | 4 ++-- .../Commodore64/Input/C64AspNetInputHandler.cs | 4 ++-- .../Input/GenericComputerAspNetInputHandler.cs | 4 ++-- .../Commodore64/Audio/C64NAudioAudioHandler.cs | 4 ++-- .../Commodore64/Input/C64SadConsoleInputHandler.cs | 4 ++-- .../Commodore64/Video/C64SadConsoleRenderer.cs | 4 ++-- .../Generic/Input/GenericSadConsoleInputHandler.cs | 4 ++-- .../Generic/Video/GenericSadConsoleRenderer.cs | 2 +- .../Commodore64/Input/C64SilkNetInputHandler.cs | 4 ++-- .../Commodore64/Video/C64SilkNetOpenGlRenderer.cs | 2 +- .../Input/GenericComputerSilkNetInputHandler.cs | 4 ++-- .../Commodore64/Video/C64SkiaRenderer.cs | 12 ++++++------ .../Generic/Video/GenericComputerSkiaRenderer.cs | 2 +- .../Highbyte.DotNet6502.Systems/Commodore64/C64.cs | 8 ++++---- .../Generic/GenericComputer.cs | 4 ++-- .../Highbyte.DotNet6502/Systems/IAudioHandler.cs | 4 ++-- .../Highbyte.DotNet6502/Systems/IInputHandler.cs | 4 ++-- .../Highbyte.DotNet6502/Systems/IRenderer.cs | 4 ++-- .../Highbyte.DotNet6502/Systems/ISystem.cs | 2 +- .../Systems/SystemRunnerBuilderTests.cs | 14 +++++++------- 25 files changed, 60 insertions(+), 62 deletions(-) diff --git a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetImGuiMenu.cs b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetImGuiMenu.cs index f31460a6..2ce8b7e8 100644 --- a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetImGuiMenu.cs +++ b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetImGuiMenu.cs @@ -145,7 +145,7 @@ public void PostOnRender() ImGui.BeginDisabled(disabled: !(EmulatorState == EmulatorState.Running || EmulatorState == EmulatorState.Paused)); ImGui.SameLine(); - if (ImGui.Button("Stats")) + if (ImGui.Button("Instrumentations")) { _silkNetWindow.ToggleStatsPanel(); } diff --git a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetImGuiStatsPanel.cs b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetImGuiStatsPanel.cs index 926eaee8..5f7667cc 100644 --- a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetImGuiStatsPanel.cs +++ b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetImGuiStatsPanel.cs @@ -39,7 +39,7 @@ public void PostOnRender() } }; - ImGui.Begin($"Stats"); + ImGui.Begin($"Instrumentations"); ImGui.PushStyleColor(ImGuiCol.Text, s_LabelColor); foreach (var line in strings) { diff --git a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetWindow.cs b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetWindow.cs index 86300113..2b768792 100644 --- a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetWindow.cs +++ b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetWindow.cs @@ -64,8 +64,6 @@ public float CanvasScale private readonly PerSecondTimedStat _updateFps = InstrumentationBag.Add($"{HostStatRootName}-OnUpdateFPS"); private readonly PerSecondTimedStat _renderFps = InstrumentationBag.Add($"{HostStatRootName}-OnRenderFPS"); - - // Render context container for SkipSharp (surface/canvas) and SilkNetOpenGl private SilkNetRenderContextContainer _silkNetRenderContextContainer; // SilkNet input handling @@ -81,7 +79,7 @@ public float CanvasScale private SilkNetImGuiMonitor _monitor; public SilkNetImGuiMonitor Monitor => _monitor; - // Stats panel + // Instrumentations panel private SilkNetImGuiStatsPanel _statsPanel; public SilkNetImGuiStatsPanel StatsPanel => _statsPanel; @@ -169,7 +167,7 @@ protected void OnLoad() protected void OnClosing() { - // Dispose Monitor/Stats panel + // Dispose Monitor/Instrumentations panel // _monitor.Cleanup(); // _statsPanel.Cleanup(); DestroyImGuiController(); @@ -491,10 +489,10 @@ private SilkNetImGuiStatsPanel CreateStatsUI() private List<(string name, IStat stat)> GetStats() { return InstrumentationBag.Stats - .Union(_systemRunner.System.Stats.Stats.Select(x => (Name: $"{HostStatRootName}-{SystemTimeStatName}-{x.Name}", x.Stat))) - .Union(_systemRunner.Renderer.Stats.Stats.Select(x => (Name: $"{HostStatRootName}-{RenderTimeStatName}-{x.Name}", x.Stat))) - .Union(_systemRunner.AudioHandler.Stats.Stats.Select(x => (Name: $"{HostStatRootName}-{AudioTimeStatName}-{x.Name}", x.Stat))) - .Union(_systemRunner.InputHandler.Stats.Stats.Select(x => (Name: $"{HostStatRootName}-{InputTimeStatName}-{x.Name}", x.Stat))) + .Union(_systemRunner.System.Instrumentations.Stats.Select(x => (Name: $"{HostStatRootName}-{SystemTimeStatName}-{x.Name}", x.Stat))) + .Union(_systemRunner.Renderer.Instrumentations.Stats.Select(x => (Name: $"{HostStatRootName}-{RenderTimeStatName}-{x.Name}", x.Stat))) + .Union(_systemRunner.AudioHandler.Instrumentations.Stats.Select(x => (Name: $"{HostStatRootName}-{AudioTimeStatName}-{x.Name}", x.Stat))) + .Union(_systemRunner.InputHandler.Instrumentations.Stats.Select(x => (Name: $"{HostStatRootName}-{InputTimeStatName}-{x.Name}", x.Stat))) .ToList(); } diff --git a/src/apps/Highbyte.DotNet6502.App.WASM/Pages/Index.razor.cs b/src/apps/Highbyte.DotNet6502.App.WASM/Pages/Index.razor.cs index 0e318159..0dc0218c 100644 --- a/src/apps/Highbyte.DotNet6502.App.WASM/Pages/Index.razor.cs +++ b/src/apps/Highbyte.DotNet6502.App.WASM/Pages/Index.razor.cs @@ -97,7 +97,7 @@ private double Scale private WasmHost? _wasmHost = default!; public WasmHost WasmHost => _wasmHost!; - private string _statsString = "Stats: calculating..."; + private string _statsString = "Instrumentations: calculating..."; private string _debugString = ""; private string _windowWidthStyle = "0px"; diff --git a/src/apps/Highbyte.DotNet6502.App.WASM/Skia/WasmHost.cs b/src/apps/Highbyte.DotNet6502.App.WASM/Skia/WasmHost.cs index 34ce71cc..8a0f3726 100644 --- a/src/apps/Highbyte.DotNet6502.App.WASM/Skia/WasmHost.cs +++ b/src/apps/Highbyte.DotNet6502.App.WASM/Skia/WasmHost.cs @@ -246,10 +246,10 @@ private string GetStats() string stats = ""; var allStats = InstrumentationBag.Stats - .Union(_systemRunner.System.Stats.Stats.Select(x => (Name: $"{HostStatRootName}-{SystemTimeStatName}-{x.Name}", x.Stat))) - .Union(_systemRunner.Renderer.Stats.Stats.Select(x => (Name: $"{HostStatRootName}-{RenderTimeStatName}-{x.Name}", x.Stat))) - .Union(_systemRunner.AudioHandler.Stats.Stats.Select(x => (Name: $"{HostStatRootName}-{AudioTimeStatName}-{x.Name}", x.Stat))) - .Union(_systemRunner.InputHandler.Stats.Stats.Select(x => (Name: $"{HostStatRootName}-{InputTimeStatName}-{x.Name}", x.Stat))) + .Union(_systemRunner.System.Instrumentations.Stats.Select(x => (Name: $"{HostStatRootName}-{SystemTimeStatName}-{x.Name}", x.Stat))) + .Union(_systemRunner.Renderer.Instrumentations.Stats.Select(x => (Name: $"{HostStatRootName}-{RenderTimeStatName}-{x.Name}", x.Stat))) + .Union(_systemRunner.AudioHandler.Instrumentations.Stats.Select(x => (Name: $"{HostStatRootName}-{AudioTimeStatName}-{x.Name}", x.Stat))) + .Union(_systemRunner.InputHandler.Instrumentations.Stats.Select(x => (Name: $"{HostStatRootName}-{InputTimeStatName}-{x.Name}", x.Stat))) .ToList(); foreach ((string name, IStat stat) in allStats.OrderBy(i => i.Name)) { diff --git a/src/libraries/Highbyte.DotNet6502.Impl.AspNet/Commodore64/Audio/C64WASMAudioHandler.cs b/src/libraries/Highbyte.DotNet6502.Impl.AspNet/Commodore64/Audio/C64WASMAudioHandler.cs index 3abbab8b..92e40252 100644 --- a/src/libraries/Highbyte.DotNet6502.Impl.AspNet/Commodore64/Audio/C64WASMAudioHandler.cs +++ b/src/libraries/Highbyte.DotNet6502.Impl.AspNet/Commodore64/Audio/C64WASMAudioHandler.cs @@ -44,8 +44,8 @@ public class C64WASMAudioHandler : IAudioHandler public List GetDebugInfo() => new(); - // Stats - public Instrumentations Stats { get; } = new(); + // Instrumentations + public Instrumentations Instrumentations { get; } = new(); public C64WASMAudioHandler(ILoggerFactory loggerFactory) { diff --git a/src/libraries/Highbyte.DotNet6502.Impl.AspNet/Commodore64/Input/C64AspNetInputHandler.cs b/src/libraries/Highbyte.DotNet6502.Impl.AspNet/Commodore64/Input/C64AspNetInputHandler.cs index 81fc6495..eaab239d 100644 --- a/src/libraries/Highbyte.DotNet6502.Impl.AspNet/Commodore64/Input/C64AspNetInputHandler.cs +++ b/src/libraries/Highbyte.DotNet6502.Impl.AspNet/Commodore64/Input/C64AspNetInputHandler.cs @@ -14,8 +14,8 @@ public class C64AspNetInputHandler : IInputHandler GetDebugInfo() => new(); - // Stats - public Instrumentations Stats { get; } = new(); + // Instrumentations + public Instrumentations Instrumentations { get; } = new(); public GenericComputerAspNetInputHandler(EmulatorInputConfig emulatorInputConfig) { diff --git a/src/libraries/Highbyte.DotNet6502.Impl.NAudio/Commodore64/Audio/C64NAudioAudioHandler.cs b/src/libraries/Highbyte.DotNet6502.Impl.NAudio/Commodore64/Audio/C64NAudioAudioHandler.cs index 0ec90e2c..e40dba7f 100644 --- a/src/libraries/Highbyte.DotNet6502.Impl.NAudio/Commodore64/Audio/C64NAudioAudioHandler.cs +++ b/src/libraries/Highbyte.DotNet6502.Impl.NAudio/Commodore64/Audio/C64NAudioAudioHandler.cs @@ -41,8 +41,8 @@ public C64NAudioAudioHandler(ILoggerFactory loggerFactory) public List GetDebugInfo() => new(); - // Stats - public Instrumentations Stats { get; } = new(); + // Instrumentations + public Instrumentations Instrumentations { get; } = new(); public void Init(C64 system, NAudioAudioHandlerContext audioHandlerContext) diff --git a/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Commodore64/Input/C64SadConsoleInputHandler.cs b/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Commodore64/Input/C64SadConsoleInputHandler.cs index 6b8b9952..05ad13cf 100644 --- a/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Commodore64/Input/C64SadConsoleInputHandler.cs +++ b/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Commodore64/Input/C64SadConsoleInputHandler.cs @@ -14,8 +14,8 @@ public class C64SadConsoleInputHandler : IInputHandler _logger; - // Stats - public Instrumentations Stats { get; } = new(); + // Instrumentations + public Instrumentations Instrumentations { get; } = new(); public C64SadConsoleInputHandler(ILoggerFactory loggerFactory) { diff --git a/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Commodore64/Video/C64SadConsoleRenderer.cs b/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Commodore64/Video/C64SadConsoleRenderer.cs index 0f2830aa..660a5c10 100644 --- a/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Commodore64/Video/C64SadConsoleRenderer.cs +++ b/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Commodore64/Video/C64SadConsoleRenderer.cs @@ -10,8 +10,8 @@ public class C64SadConsoleRenderer : IRenderer private SadConsoleRenderContext _sadConsoleRenderContext = default!; private C64SadConsoleColors _c64SadConsoleColors = default!; - // Stats - public Instrumentations Stats { get; } = new(); + // Instrumentations + public Instrumentations Instrumentations { get; } = new(); public C64SadConsoleRenderer() { diff --git a/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Generic/Input/GenericSadConsoleInputHandler.cs b/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Generic/Input/GenericSadConsoleInputHandler.cs index 25ecdf89..f0a13016 100644 --- a/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Generic/Input/GenericSadConsoleInputHandler.cs +++ b/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Generic/Input/GenericSadConsoleInputHandler.cs @@ -15,8 +15,8 @@ public class GenericSadConsoleInputHandler : IInputHandler GetDebugInfo() => new(); - // Stats - public Instrumentations Stats { get; } = new(); + // Instrumentations + public Instrumentations Instrumentations { get; } = new(); public GenericSadConsoleInputHandler(EmulatorInputConfig emulatorInputConfig, ILoggerFactory loggerFactory) diff --git a/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Generic/Video/GenericSadConsoleRenderer.cs b/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Generic/Video/GenericSadConsoleRenderer.cs index 072addac..4c09a14e 100644 --- a/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Generic/Video/GenericSadConsoleRenderer.cs +++ b/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Generic/Video/GenericSadConsoleRenderer.cs @@ -11,7 +11,7 @@ public class GenericSadConsoleRenderer : IRenderer GetDebugInfo() => new(); - // Stats - public Instrumentations Stats { get; } = new(); + // Instrumentations + public Instrumentations Instrumentations { get; } = new(); public C64SilkNetInputHandler(ILoggerFactory loggerFactory, C64SilkNetConfig c64SilkNetConfig) diff --git a/src/libraries/Highbyte.DotNet6502.Impl.SilkNet/Commodore64/Video/C64SilkNetOpenGlRenderer.cs b/src/libraries/Highbyte.DotNet6502.Impl.SilkNet/Commodore64/Video/C64SilkNetOpenGlRenderer.cs index eff86e61..941eeee5 100644 --- a/src/libraries/Highbyte.DotNet6502.Impl.SilkNet/Commodore64/Video/C64SilkNetOpenGlRenderer.cs +++ b/src/libraries/Highbyte.DotNet6502.Impl.SilkNet/Commodore64/Video/C64SilkNetOpenGlRenderer.cs @@ -19,7 +19,7 @@ public class C64SilkNetOpenGlRenderer : IRenderer GetDebugInfo() => new(); - // Stats - public Instrumentations Stats { get; } = new(); + // Instrumentations + public Instrumentations Instrumentations { get; } = new(); public GenericComputerSilkNetInputHandler(EmulatorInputConfig emulatorInputConfig) { diff --git a/src/libraries/Highbyte.DotNet6502.Impl.Skia/Commodore64/Video/C64SkiaRenderer.cs b/src/libraries/Highbyte.DotNet6502.Impl.Skia/Commodore64/Video/C64SkiaRenderer.cs index a335a02a..c367bb85 100644 --- a/src/libraries/Highbyte.DotNet6502.Impl.Skia/Commodore64/Video/C64SkiaRenderer.cs +++ b/src/libraries/Highbyte.DotNet6502.Impl.Skia/Commodore64/Video/C64SkiaRenderer.cs @@ -34,8 +34,8 @@ public class C64SkiaRenderer : IRenderer // Sprite drawing variables private readonly SKImage[] _spriteImages; - // Stats - public Instrumentations Stats { get; } = new(); + // Instrumentations + public Instrumentations Instrumentations { get; } = new(); private const string StatsCategory = "SkiaSharp-Custom"; private readonly ElapsedMillisecondsTimedStat _borderStat; private readonly ElapsedMillisecondsTimedStat _backgroundStat; @@ -47,10 +47,10 @@ public C64SkiaRenderer() _charGen = new CharGen(); _spriteImages = new SKImage[Vic2SpriteManager.NUMBERS_OF_SPRITES]; - _backgroundStat = Stats.Add($"{StatsCategory}-Background"); - _borderStat = Stats.Add($"{StatsCategory}-Border"); - _spritesStat = Stats.Add($"{StatsCategory}-Sprites"); - _textScreenStat = Stats.Add($"{StatsCategory}-TextScreen"); + _backgroundStat = Instrumentations.Add($"{StatsCategory}-Background"); + _borderStat = Instrumentations.Add($"{StatsCategory}-Border"); + _spritesStat = Instrumentations.Add($"{StatsCategory}-Sprites"); + _textScreenStat = Instrumentations.Add($"{StatsCategory}-TextScreen"); } public void Init(C64 c64, SkiaRenderContext skiaRenderContext) diff --git a/src/libraries/Highbyte.DotNet6502.Impl.Skia/Generic/Video/GenericComputerSkiaRenderer.cs b/src/libraries/Highbyte.DotNet6502.Impl.Skia/Generic/Video/GenericComputerSkiaRenderer.cs index 1fa0aec0..6fb381de 100644 --- a/src/libraries/Highbyte.DotNet6502.Impl.Skia/Generic/Video/GenericComputerSkiaRenderer.cs +++ b/src/libraries/Highbyte.DotNet6502.Impl.Skia/Generic/Video/GenericComputerSkiaRenderer.cs @@ -18,7 +18,7 @@ public class GenericComputerSkiaRenderer : IRenderer($"{StatsCategory}-SpriteCollision"); - _audioStat = Stats.Add($"{StatsCategory}-Audio"); + _spriteCollisionStat = Instrumentations.Add($"{StatsCategory}-SpriteCollision"); + _audioStat = Instrumentations.Add($"{StatsCategory}-Audio"); } public static C64 BuildC64(C64Config c64Config, ILoggerFactory loggerFactory) diff --git a/src/libraries/Highbyte.DotNet6502.Systems/Generic/GenericComputer.cs b/src/libraries/Highbyte.DotNet6502.Systems/Generic/GenericComputer.cs index 03c23452..fe4b37e7 100644 --- a/src/libraries/Highbyte.DotNet6502.Systems/Generic/GenericComputer.cs +++ b/src/libraries/Highbyte.DotNet6502.Systems/Generic/GenericComputer.cs @@ -41,8 +41,8 @@ public class GenericComputer : ISystem, ITextMode, IScreen private GenericComputerConfig _genericComputerConfig; private readonly LegacyExecEvaluator _oneFrameExecEvaluator; - // Stats - public Instrumentations Stats { get; } = new(); + // Instrumentations + public Instrumentations Instrumentations { get; } = new(); public GenericComputer() : this(new GenericComputerConfig(), new NullLoggerFactory()) { } diff --git a/src/libraries/Highbyte.DotNet6502/Systems/IAudioHandler.cs b/src/libraries/Highbyte.DotNet6502/Systems/IAudioHandler.cs index 352c698b..0d97b820 100644 --- a/src/libraries/Highbyte.DotNet6502/Systems/IAudioHandler.cs +++ b/src/libraries/Highbyte.DotNet6502/Systems/IAudioHandler.cs @@ -11,7 +11,7 @@ public interface IAudioHandler void StopPlaying(); void PausePlaying(); List GetDebugInfo(); - Instrumentations Stats { get; } + Instrumentations Instrumentations { get; } } public interface IAudioHandler : IAudioHandler @@ -51,5 +51,5 @@ public void StopPlaying() public List GetDebugInfo() => new(); - public Instrumentations Stats { get; } = new(); + public Instrumentations Instrumentations { get; } = new(); } diff --git a/src/libraries/Highbyte.DotNet6502/Systems/IInputHandler.cs b/src/libraries/Highbyte.DotNet6502/Systems/IInputHandler.cs index 03b0bdbb..4cbf1b4f 100644 --- a/src/libraries/Highbyte.DotNet6502/Systems/IInputHandler.cs +++ b/src/libraries/Highbyte.DotNet6502/Systems/IInputHandler.cs @@ -8,7 +8,7 @@ public interface IInputHandler void ProcessInput(ISystem system); List GetDebugInfo(); - Instrumentations Stats { get; } + Instrumentations Instrumentations { get; } } public interface IInputHandler : IInputHandler @@ -31,5 +31,5 @@ public void ProcessInput(ISystem system) } public List GetDebugInfo() => new(); - public Instrumentations Stats { get; } = new(); + public Instrumentations Instrumentations { get; } = new(); } diff --git a/src/libraries/Highbyte.DotNet6502/Systems/IRenderer.cs b/src/libraries/Highbyte.DotNet6502/Systems/IRenderer.cs index 611f6de1..81a3da8c 100644 --- a/src/libraries/Highbyte.DotNet6502/Systems/IRenderer.cs +++ b/src/libraries/Highbyte.DotNet6502/Systems/IRenderer.cs @@ -7,7 +7,7 @@ public interface IRenderer void Init(ISystem system, IRenderContext renderContext); void Draw(ISystem system); - Instrumentations Stats { get; } + Instrumentations Instrumentations { get; } } public interface IRenderer : IRenderer @@ -20,7 +20,7 @@ public interface IRenderer : IRenderer public class NullRenderer : IRenderer { - public Instrumentations Stats { get; } = new(); + public Instrumentations Instrumentations { get; } = new(); public void Init(ISystem system, IRenderContext renderContext) { diff --git a/src/libraries/Highbyte.DotNet6502/Systems/ISystem.cs b/src/libraries/Highbyte.DotNet6502/Systems/ISystem.cs index b0423974..619be940 100644 --- a/src/libraries/Highbyte.DotNet6502/Systems/ISystem.cs +++ b/src/libraries/Highbyte.DotNet6502/Systems/ISystem.cs @@ -20,5 +20,5 @@ ExecEvaluatorTriggerResult ExecuteOneInstruction( out InstructionExecResult instructionExecResult, IExecEvaluator? execEvaluator = null); - Instrumentations Stats { get; } + Instrumentations Instrumentations { get; } } diff --git a/tests/Highbyte.DotNet6502.Tests/Systems/SystemRunnerBuilderTests.cs b/tests/Highbyte.DotNet6502.Tests/Systems/SystemRunnerBuilderTests.cs index 88a6812c..ca5e3a2b 100644 --- a/tests/Highbyte.DotNet6502.Tests/Systems/SystemRunnerBuilderTests.cs +++ b/tests/Highbyte.DotNet6502.Tests/Systems/SystemRunnerBuilderTests.cs @@ -113,7 +113,7 @@ public class TestSystem : ISystem public IScreen Screen => throw new NotImplementedException(); - public Instrumentations Stats { get; } = new(); + public Instrumentations Instrumentations { get; } = new(); public ExecEvaluatorTriggerResult ExecuteOneFrame(SystemRunner systemRunner, IExecEvaluator? execEvaluator = null) { @@ -141,7 +141,7 @@ public void Init(TestSystem system, IRenderContext renderContext) public void Init(ISystem system, IRenderContext renderContext) { } - public Instrumentations Stats { get; } = new(); + public Instrumentations Instrumentations { get; } = new(); } public class TestRendererNonGeneric : IRenderer { @@ -151,7 +151,7 @@ public void Draw(ISystem system) public void Init(ISystem system, IRenderContext renderContext) { } - public Instrumentations Stats { get; } = new(); + public Instrumentations Instrumentations { get; } = new(); } public class TestInputHandler : IInputHandler @@ -171,7 +171,7 @@ public void ProcessInput(ISystem system) public List GetDebugInfo() => new(); - public Instrumentations Stats { get; } = new(); + public Instrumentations Instrumentations { get; } = new(); } public class TestInputHandlerNonGeneric : IInputHandler @@ -184,7 +184,7 @@ public void ProcessInput(ISystem system) { } public List GetDebugInfo() => new List(); - public Instrumentations Stats { get; } = new(); + public Instrumentations Instrumentations { get; } = new(); } public class TestAudioHandler : IAudioHandler @@ -212,7 +212,7 @@ public void StopPlaying() } public List GetDebugInfo() => new(); - public Instrumentations Stats { get; } = new(); + public Instrumentations Instrumentations { get; } = new(); } @@ -235,6 +235,6 @@ public void StopPlaying() } public List GetDebugInfo() => new(); - public Instrumentations Stats { get; } = new(); + public Instrumentations Instrumentations { get; } = new(); } From 9ef878c26633cfeb333b8f2dd354f67f3a082133 Mon Sep 17 00:00:00 2001 From: Highbyte Date: Wed, 29 Nov 2023 06:16:46 +0100 Subject: [PATCH 04/10] Revert dialog name change mistake --- .../Highbyte.DotNet6502.App.SilkNetNative/SilkNetImGuiMenu.cs | 2 +- .../SilkNetImGuiStatsPanel.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetImGuiMenu.cs b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetImGuiMenu.cs index 2ce8b7e8..f31460a6 100644 --- a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetImGuiMenu.cs +++ b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetImGuiMenu.cs @@ -145,7 +145,7 @@ public void PostOnRender() ImGui.BeginDisabled(disabled: !(EmulatorState == EmulatorState.Running || EmulatorState == EmulatorState.Paused)); ImGui.SameLine(); - if (ImGui.Button("Instrumentations")) + if (ImGui.Button("Stats")) { _silkNetWindow.ToggleStatsPanel(); } diff --git a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetImGuiStatsPanel.cs b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetImGuiStatsPanel.cs index 5f7667cc..926eaee8 100644 --- a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetImGuiStatsPanel.cs +++ b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetImGuiStatsPanel.cs @@ -39,7 +39,7 @@ public void PostOnRender() } }; - ImGui.Begin($"Instrumentations"); + ImGui.Begin($"Stats"); ImGui.PushStyleColor(ImGuiCol.Text, s_LabelColor); foreach (var line in strings) { From 265301e52bbac60fe3b4aaaca3b87b3855ea1336 Mon Sep 17 00:00:00 2001 From: Highbyte Date: Wed, 29 Nov 2023 15:39:19 +0100 Subject: [PATCH 05/10] Instrumentation tests --- .../Commodore64/C64.cs | 2 + .../Instrumentation/Stats/AveragedStat.cs | 2 - .../Stats/DisposableCallback.cs | 8 ++- .../Stats/ElapsedMillisecondsStat.cs | 31 -------- .../Stats/ElapsedMillisecondsTimedStat.cs | 35 +++++++--- .../Instrumentation/Stats/IStat.cs | 1 - .../Instrumentation/AveragedStatTest.cs | 34 +++++++++ .../ElapsedMillisecondsTimedStatTest.cs | 55 +++++++++++++++ .../Instrumentation/InstrumentationsTest.cs | 70 +++++++++++++++++++ .../Instrumentation/TestStat.cs | 24 +++++++ 10 files changed, 216 insertions(+), 46 deletions(-) delete mode 100644 src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/ElapsedMillisecondsStat.cs create mode 100644 tests/Highbyte.DotNet6502.Tests/Instrumentation/AveragedStatTest.cs create mode 100644 tests/Highbyte.DotNet6502.Tests/Instrumentation/ElapsedMillisecondsTimedStatTest.cs create mode 100644 tests/Highbyte.DotNet6502.Tests/Instrumentation/InstrumentationsTest.cs create mode 100644 tests/Highbyte.DotNet6502.Tests/Instrumentation/TestStat.cs diff --git a/src/libraries/Highbyte.DotNet6502.Systems/Commodore64/C64.cs b/src/libraries/Highbyte.DotNet6502.Systems/Commodore64/C64.cs index 382f3a5c..0a8e2f84 100644 --- a/src/libraries/Highbyte.DotNet6502.Systems/Commodore64/C64.cs +++ b/src/libraries/Highbyte.DotNet6502.Systems/Commodore64/C64.cs @@ -87,6 +87,8 @@ public ExecEvaluatorTriggerResult ExecuteOneFrame( } } + _audioStat.Stop(); // Stop audio stat (was continiously updated after each instruction) + // Update sprite collision state using (_spriteCollisionStat.Measure()) { diff --git a/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/AveragedStat.cs b/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/AveragedStat.cs index 7d320592..778bdc43 100644 --- a/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/AveragedStat.cs +++ b/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/AveragedStat.cs @@ -14,9 +14,7 @@ protected void SetValue(double value) if (Value == null) Value = value; else - { Value = (Value * (_sampleCount - 1) + value) / _sampleCount; - } } public abstract string GetDescription(); diff --git a/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/DisposableCallback.cs b/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/DisposableCallback.cs index 70415f99..4b877a80 100644 --- a/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/DisposableCallback.cs +++ b/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/DisposableCallback.cs @@ -4,8 +4,14 @@ namespace Highbyte.DotNet6502.Instrumentation.Stats; public class DisposableCallback : IDisposable { public event EventHandler? Disposing; + public bool Cont = false; public void Dispose() { - Disposing?.Invoke(this, EventArgs.Empty); + Disposing?.Invoke(this, new DisposableCallbackEventArgs { Cont = Cont }); } } +public class DisposableCallbackEventArgs : EventArgs +{ + public bool Cont { get; set; } +} + diff --git a/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/ElapsedMillisecondsStat.cs b/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/ElapsedMillisecondsStat.cs deleted file mode 100644 index faf5111b..00000000 --- a/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/ElapsedMillisecondsStat.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace Highbyte.DotNet6502.Instrumentation.Stats; - -public class ElapsedMillisecondsStat : AveragedStat -{ - private double _currentMs; - public ElapsedMillisecondsStat() - : base(10) // Average over 10 samples - { - _currentMs = 0; - } - public void Add(double ms) => _currentMs += ms; - public void Set(double ms) => _currentMs = ms; - public void UpdateStat() - { - SetValue(_currentMs); - _currentMs = 0; - } - - public override string GetDescription() - { - if (Value == null) - return "null"; - //double ms = Value.Value / TimeSpan.TicksPerMillisecond; // 10000 ticks per millisecond - var ms = Value.Value; - - if (ms < 0.01) - return "< 0.01ms"; - return Math.Round(ms, 2).ToString("0.00") + "ms"; - - } -} diff --git a/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/ElapsedMillisecondsTimedStat.cs b/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/ElapsedMillisecondsTimedStat.cs index 5ac6ca23..eb01a169 100644 --- a/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/ElapsedMillisecondsTimedStat.cs +++ b/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/ElapsedMillisecondsTimedStat.cs @@ -8,12 +8,15 @@ public class ElapsedMillisecondsTimedStat : AveragedStat { private readonly Stopwatch _sw; private readonly DisposableCallback _disposableCallback; - public ElapsedMillisecondsTimedStat() - : base(10) // Average over x samples + + public ElapsedMillisecondsTimedStat() : this(10) { } + + public ElapsedMillisecondsTimedStat(int samples) + : base(samples) // Average over x samples { _sw = new Stopwatch(); _disposableCallback = new DisposableCallback(); - _disposableCallback.Disposing += (o, e) => Stop(); + _disposableCallback.Disposing += (object? o, EventArgs e) => Stop(((DisposableCallbackEventArgs)e).Cont); } public void Reset() @@ -29,27 +32,37 @@ public void Start(bool cont = false) _sw.Restart(); } - public void Stop() + public void Stop(bool cont = false) { _sw.Stop(); - //SetValue(_sw.ElapsedMilliseconds); - SetValue(_sw.Elapsed.Ticks); + if (!cont) + { + //SetValue(_sw.ElapsedMilliseconds); + SetValue(_sw.Elapsed.Ticks); + } } + public IDisposable Measure(bool cont = false) { Start(cont); return _disposableCallback; } - public override string GetDescription() + + public double? GetStatMilliseconds() { if (Value == null) - return "null"; + return null; + return Value.Value / TimeSpan.TicksPerMillisecond; // 10000 ticks per millisecond + } - //double ms = Value.Value / 10000.0d; // 10000 ticks per millisecond - var ms = Value.Value / TimeSpan.TicksPerMillisecond; // 10000 ticks per millisecond + public override string GetDescription() + { + var ms = GetStatMilliseconds(); + if (ms == null) + return "null"; if (ms < 0.01) return "< 0.01ms"; - return Math.Round(ms, 2).ToString("0.00") + "ms"; + return Math.Round(ms.Value, 2).ToString("0.00") + "ms"; } } diff --git a/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/IStat.cs b/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/IStat.cs index 0bb0d864..3bf9c364 100644 --- a/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/IStat.cs +++ b/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/IStat.cs @@ -4,6 +4,5 @@ namespace Highbyte.DotNet6502.Instrumentation.Stats; public interface IStat { string GetDescription(); - bool ShouldShow(); } diff --git a/tests/Highbyte.DotNet6502.Tests/Instrumentation/AveragedStatTest.cs b/tests/Highbyte.DotNet6502.Tests/Instrumentation/AveragedStatTest.cs new file mode 100644 index 00000000..3af053a7 --- /dev/null +++ b/tests/Highbyte.DotNet6502.Tests/Instrumentation/AveragedStatTest.cs @@ -0,0 +1,34 @@ +namespace Highbyte.DotNet6502.Tests.Instrumentation +{ + public class AveragedStatTest + { + [Fact] + public void Value_WhenCalled_ReturnsValue() + { + // Arrange + var stat = new TestAveragedStat(10); + stat.UpdateStat(1.0); + + // Act + var value = stat.Value; + + // Assert + Assert.Equal(1.0, value); + } + + [Fact] + public void Value_WhenCalled_ReturnsValue_Average() + { + // Arrange + var stat = new TestAveragedStat(sampleCount: 2); + stat.UpdateStat(1.0); + stat.UpdateStat(2.0); + + // Act + var value = stat.Value; + + // Assert + Assert.Equal(1.5, value); + } + } +} diff --git a/tests/Highbyte.DotNet6502.Tests/Instrumentation/ElapsedMillisecondsTimedStatTest.cs b/tests/Highbyte.DotNet6502.Tests/Instrumentation/ElapsedMillisecondsTimedStatTest.cs new file mode 100644 index 00000000..c14c1105 --- /dev/null +++ b/tests/Highbyte.DotNet6502.Tests/Instrumentation/ElapsedMillisecondsTimedStatTest.cs @@ -0,0 +1,55 @@ +using Highbyte.DotNet6502.Instrumentation.Stats; + +namespace Highbyte.DotNet6502.Tests.Instrumentation +{ + public class ElapsedMillisecondsTimedStatTest + { + [Fact] + public void Measure_WhenUsed_Returns_ExecutionTime_In_GetStatMilliseconds() + { + // Arrange + var stat = new ElapsedMillisecondsTimedStat(samples: 1); + + int sleepMs = 2; + using (stat.Measure()) + { + Thread.Sleep(sleepMs); + } + + // Act + var elapsedMs = stat.GetStatMilliseconds(); + + // Assert + Assert.True(elapsedMs >= sleepMs); + Assert.True(elapsedMs < sleepMs + 1); + } + + [Fact] + public void Measure_WhenUsed_With_Cont_Returns_ExecutionTime_In_GetStatMilliseconds() + { + // Arrange + var stat = new ElapsedMillisecondsTimedStat(samples: 1); + + int sleepMs = 2; + using (stat.Measure()) + { + Thread.Sleep(sleepMs); + } + int sleepNextMs = 3; + using (stat.Measure(cont: true)) + { + Thread.Sleep(sleepNextMs); + } + + // Act + var elapsedMs = stat.GetStatMilliseconds(); + + // Assert + Assert.True(elapsedMs >= (sleepMs + sleepNextMs)); + +#if !DEBUG // In debug mode, the elapsed time may not accurate enough to make this test pass (if breakpoint is hit during sleep) + Assert.True(elapsedMs < (sleepMs + sleepNextMs) + 1); +#endif + } + } +} diff --git a/tests/Highbyte.DotNet6502.Tests/Instrumentation/InstrumentationsTest.cs b/tests/Highbyte.DotNet6502.Tests/Instrumentation/InstrumentationsTest.cs new file mode 100644 index 00000000..7fd84cc0 --- /dev/null +++ b/tests/Highbyte.DotNet6502.Tests/Instrumentation/InstrumentationsTest.cs @@ -0,0 +1,70 @@ +using Highbyte.DotNet6502.Instrumentation; + +namespace Highbyte.DotNet6502.Tests.Instrumentation +{ + public class InstrumentationsTest + { + + [Fact] + public void Add_WhenCalled_AddsStatToStatsList() + { + // Arrange + var instrumentations = new Instrumentations(); + + // Act + var stat = new TestStat(); + var addedStat = instrumentations.Add("stat1", stat); + + // Assert + Assert.Single(instrumentations.Stats); + Assert.Equal(addedStat, stat); + Assert.Equal(addedStat, instrumentations.Stats.First().Stat); + Assert.Equal("stat1", instrumentations.Stats.First().Name); + } + + [Fact] + public void Add_WhenCalled_AddsStatToStatsList2() + { + // Arrange + var instrumentations = new Instrumentations(); + + // Act + var addedStat = instrumentations.Add("stat1"); + + // Assert + Assert.Single(instrumentations.Stats); + Assert.Equal(addedStat, instrumentations.Stats.First().Stat); + Assert.Equal("stat1", instrumentations.Stats.First().Name); + } + + [Fact] + public void Remove_WhenCalled_RemovesStatFromStatsList() + { + // Arrange + var instrumentations = new Instrumentations(); + instrumentations.Add("stat1", new TestStat()); + instrumentations.Add("stat2", new TestStat()); + + // Act + instrumentations.Remove("stat1"); + + // Assert + Assert.Single(instrumentations.Stats); + } + + [Fact] + public void Clear_WhenCalled_ClearsStatsList() + { + // Arrange + var instrumentations = new Instrumentations(); + instrumentations.Add("stat1", new TestStat()); + instrumentations.Add("stat2", new TestStat()); + + // Act + instrumentations.Clear(); + + // Assert + Assert.Empty(instrumentations.Stats); + } + } +} diff --git a/tests/Highbyte.DotNet6502.Tests/Instrumentation/TestStat.cs b/tests/Highbyte.DotNet6502.Tests/Instrumentation/TestStat.cs new file mode 100644 index 00000000..5e1bf73d --- /dev/null +++ b/tests/Highbyte.DotNet6502.Tests/Instrumentation/TestStat.cs @@ -0,0 +1,24 @@ +using Highbyte.DotNet6502.Instrumentation.Stats; + +namespace Highbyte.DotNet6502.Tests.Instrumentation +{ + internal class TestStat : IStat + { + public string GetDescription() => "TestStat"; + public bool ShouldShow() => true; + } + + internal class TestAveragedStat : AveragedStat + { + public TestAveragedStat(int sampleCount) : base(sampleCount) + { + } + + public void UpdateStat(double value) + { + SetValue(value); + } + + public override string GetDescription() => "TestAveragedStat"; + } +} From f27c7605629deed233df565437322f40ac7016ea Mon Sep 17 00:00:00 2001 From: Highbyte Date: Wed, 29 Nov 2023 15:52:21 +0100 Subject: [PATCH 06/10] Fix instrumentation bug --- src/libraries/Highbyte.DotNet6502.Systems/Commodore64/C64.cs | 3 +++ .../Instrumentation/Stats/DisposableCallback.cs | 3 ++- .../Instrumentation/Stats/ElapsedMillisecondsTimedStat.cs | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libraries/Highbyte.DotNet6502.Systems/Commodore64/C64.cs b/src/libraries/Highbyte.DotNet6502.Systems/Commodore64/C64.cs index 0a8e2f84..d977afe4 100644 --- a/src/libraries/Highbyte.DotNet6502.Systems/Commodore64/C64.cs +++ b/src/libraries/Highbyte.DotNet6502.Systems/Commodore64/C64.cs @@ -146,6 +146,9 @@ private C64(ILogger logger) { _logger = logger; _spriteCollisionStat = Instrumentations.Add($"{StatsCategory}-SpriteCollision"); + + //_audioStat = new ElapsedMillisecondsTimedStat(samples: 1); + //Instrumentations.Add($"{StatsCategory}-Audio", _audioStat); _audioStat = Instrumentations.Add($"{StatsCategory}-Audio"); } diff --git a/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/DisposableCallback.cs b/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/DisposableCallback.cs index 4b877a80..d6801d1b 100644 --- a/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/DisposableCallback.cs +++ b/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/DisposableCallback.cs @@ -4,7 +4,8 @@ namespace Highbyte.DotNet6502.Instrumentation.Stats; public class DisposableCallback : IDisposable { public event EventHandler? Disposing; - public bool Cont = false; + public bool Cont; + public void Dispose() { Disposing?.Invoke(this, new DisposableCallbackEventArgs { Cont = Cont }); diff --git a/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/ElapsedMillisecondsTimedStat.cs b/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/ElapsedMillisecondsTimedStat.cs index eb01a169..f44a0c8d 100644 --- a/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/ElapsedMillisecondsTimedStat.cs +++ b/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/ElapsedMillisecondsTimedStat.cs @@ -45,6 +45,7 @@ public void Stop(bool cont = false) public IDisposable Measure(bool cont = false) { Start(cont); + _disposableCallback.Cont = cont; return _disposableCallback; } From 7ccb52d21254623ae730a501f2df6bffc1d9f504 Mon Sep 17 00:00:00 2001 From: Highbyte Date: Wed, 29 Nov 2023 16:12:49 +0100 Subject: [PATCH 07/10] FIx tests --- .../ElapsedMillisecondsTimedStatTest.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/Highbyte.DotNet6502.Tests/Instrumentation/ElapsedMillisecondsTimedStatTest.cs b/tests/Highbyte.DotNet6502.Tests/Instrumentation/ElapsedMillisecondsTimedStatTest.cs index c14c1105..044772a5 100644 --- a/tests/Highbyte.DotNet6502.Tests/Instrumentation/ElapsedMillisecondsTimedStatTest.cs +++ b/tests/Highbyte.DotNet6502.Tests/Instrumentation/ElapsedMillisecondsTimedStatTest.cs @@ -21,7 +21,10 @@ public void Measure_WhenUsed_Returns_ExecutionTime_In_GetStatMilliseconds() // Assert Assert.True(elapsedMs >= sleepMs); - Assert.True(elapsedMs < sleepMs + 1); +#if !DEBUG + // In debug mode, the elapsed time may not accurate enough to make this test pass (if breakpoint is hit during sleep) + Assert.True(elapsedMs < sleepMs + 10); +#endif } [Fact] @@ -31,7 +34,7 @@ public void Measure_WhenUsed_With_Cont_Returns_ExecutionTime_In_GetStatMilliseco var stat = new ElapsedMillisecondsTimedStat(samples: 1); int sleepMs = 2; - using (stat.Measure()) + using (stat.Measure(cont: true)) { Thread.Sleep(sleepMs); } @@ -40,6 +43,7 @@ public void Measure_WhenUsed_With_Cont_Returns_ExecutionTime_In_GetStatMilliseco { Thread.Sleep(sleepNextMs); } + stat.Stop(); // Act var elapsedMs = stat.GetStatMilliseconds(); @@ -47,8 +51,9 @@ public void Measure_WhenUsed_With_Cont_Returns_ExecutionTime_In_GetStatMilliseco // Assert Assert.True(elapsedMs >= (sleepMs + sleepNextMs)); -#if !DEBUG // In debug mode, the elapsed time may not accurate enough to make this test pass (if breakpoint is hit during sleep) - Assert.True(elapsedMs < (sleepMs + sleepNextMs) + 1); +#if !DEBUG + // In debug mode, the elapsed time may not accurate enough to make this test pass (if breakpoint is hit during sleep) + Assert.True(elapsedMs < (sleepMs + sleepNextMs) + 10); #endif } } From e303aa0c350b8a9a936eec92ad4307de7d501b8f Mon Sep 17 00:00:00 2001 From: Highbyte Date: Wed, 29 Nov 2023 16:31:58 +0100 Subject: [PATCH 08/10] Add tests --- .../Instrumentation/InstrumentationBagTest.cs | 74 +++++++++++++++++++ .../Instrumentation/PerSecondTimedStatTest.cs | 27 +++++++ 2 files changed, 101 insertions(+) create mode 100644 tests/Highbyte.DotNet6502.Tests/Instrumentation/InstrumentationBagTest.cs create mode 100644 tests/Highbyte.DotNet6502.Tests/Instrumentation/PerSecondTimedStatTest.cs diff --git a/tests/Highbyte.DotNet6502.Tests/Instrumentation/InstrumentationBagTest.cs b/tests/Highbyte.DotNet6502.Tests/Instrumentation/InstrumentationBagTest.cs new file mode 100644 index 00000000..7252e3e7 --- /dev/null +++ b/tests/Highbyte.DotNet6502.Tests/Instrumentation/InstrumentationBagTest.cs @@ -0,0 +1,74 @@ +using Highbyte.DotNet6502.Instrumentation; + +namespace Highbyte.DotNet6502.Tests.Instrumentation +{ + /// + /// InstrumentationBag is a static class that uses Instrumentations class under the hood. + /// + public class InstrumentationBagTest + { + + [Fact] + public void Add_WhenCalled_AddsStatToStatsList() + { + // Arrange + InstrumentationBag.Clear(); + + // Act + var stat = new TestStat(); + var addedStat = InstrumentationBag.Add("stat1", stat); + + // Assert + Assert.Single(InstrumentationBag.Stats); + Assert.Equal(addedStat, stat); + Assert.Equal(addedStat, InstrumentationBag.Stats.First().Stat); + Assert.Equal("stat1", InstrumentationBag.Stats.First().Name); + } + + [Fact] + public void Add_WhenCalled_AddsStatToStatsList2() + { + // Arrange + InstrumentationBag.Clear(); + + // Act + var addedStat = InstrumentationBag.Add("stat1"); + + // Assert + Assert.Single(InstrumentationBag.Stats); + Assert.Equal(addedStat, InstrumentationBag.Stats.First().Stat); + Assert.Equal("stat1", InstrumentationBag.Stats.First().Name); + } + + [Fact] + public void Remove_WhenCalled_RemovesStatFromStatsList() + { + // Arrange + InstrumentationBag.Clear(); + InstrumentationBag.Add("stat1", new TestStat()); + InstrumentationBag.Add("stat2", new TestStat()); + + // Act + InstrumentationBag.Remove("stat1"); + + // Assert + Assert.Single(InstrumentationBag.Stats); + } + + [Fact] + public void Clear_WhenCalled_ClearsStatsList() + { + // Arrange + InstrumentationBag.Clear(); + + InstrumentationBag.Add("stat1", new TestStat()); + InstrumentationBag.Add("stat2", new TestStat()); + + // Act + InstrumentationBag.Clear(); + + // Assert + Assert.Empty(InstrumentationBag.Stats); + } + } +} diff --git a/tests/Highbyte.DotNet6502.Tests/Instrumentation/PerSecondTimedStatTest.cs b/tests/Highbyte.DotNet6502.Tests/Instrumentation/PerSecondTimedStatTest.cs new file mode 100644 index 00000000..bf2f7b9d --- /dev/null +++ b/tests/Highbyte.DotNet6502.Tests/Instrumentation/PerSecondTimedStatTest.cs @@ -0,0 +1,27 @@ +using Highbyte.DotNet6502.Instrumentation.Stats; + +namespace Highbyte.DotNet6502.Tests.Instrumentation +{ + public class PerSecondTimedStatTest + { + // Test PerSecondTimedStatTest that it calculates per second correctly. + // This test is not very accurate, but it should be good enough to catch any major errors. + [Fact] + public void Update_WhenUsed_Returns_Correct_PerSecond_Value() + { + // Arrange + var stat = new PerSecondTimedStat(); + + // Act + stat.Update(); + int sleepMs = 16; // 16 ms should give arround 60 FPS + Thread.Sleep(sleepMs); + stat.Update(); + + // Assert + var perSecond = stat.Value; + Assert.True(perSecond >= 55); + Assert.True(perSecond < 65); + } + } +} From 022a2c17be4aef7360ae5409a7e0480c67c93690 Mon Sep 17 00:00:00 2001 From: Highbyte Date: Wed, 29 Nov 2023 16:57:59 +0100 Subject: [PATCH 09/10] Add tests --- src/libraries/Highbyte.DotNet6502/Data.cs | 17 -------- .../Stats/ElapsedMillisecondsTimedStat.cs | 5 +++ .../Stats/PerSecondTimedStat.cs | 7 ++++ .../ElapsedMillisecondsTimedStatTest.cs | 41 +++++++++++++++++++ .../Instrumentation/PerSecondTimedStatTest.cs | 41 +++++++++++++++++++ 5 files changed, 94 insertions(+), 17 deletions(-) delete mode 100644 src/libraries/Highbyte.DotNet6502/Data.cs diff --git a/src/libraries/Highbyte.DotNet6502/Data.cs b/src/libraries/Highbyte.DotNet6502/Data.cs deleted file mode 100644 index b2dee4fa..00000000 --- a/src/libraries/Highbyte.DotNet6502/Data.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Highbyte.DotNet6502; - -public static class Data -{ - public static byte[] GetResetVectorCode(ushort userCodeAddress) - { - List list = new() - { - // TODO: Add init code for setting SP, I flag, etc, as a normal 6502 would do in the reset vector - // End with jumping to address where our actual user code lives. Typically this would be the address of Basic. - (byte)OpCodeId.JMP_ABS - }; - List code = list; - code.AddRange(userCodeAddress.ToLittleEndianBytes()); - return code.ToArray(); - } -} diff --git a/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/ElapsedMillisecondsTimedStat.cs b/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/ElapsedMillisecondsTimedStat.cs index f44a0c8d..289d217b 100644 --- a/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/ElapsedMillisecondsTimedStat.cs +++ b/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/ElapsedMillisecondsTimedStat.cs @@ -66,4 +66,9 @@ public override string GetDescription() return "< 0.01ms"; return Math.Round(ms.Value, 2).ToString("0.00") + "ms"; } + + public void SetFakeMSValue(double ms) + { + SetValue(ms); + } } diff --git a/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/PerSecondTimedStat.cs b/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/PerSecondTimedStat.cs index 96ab07b9..5aada09c 100644 --- a/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/PerSecondTimedStat.cs +++ b/src/libraries/Highbyte.DotNet6502/Instrumentation/Stats/PerSecondTimedStat.cs @@ -28,6 +28,7 @@ public void Update() _sw.Restart(); } + public override string GetDescription() { if (Value == null) @@ -36,4 +37,10 @@ public override string GetDescription() return "< 0.01"; return Math.Round(Value ?? 0, 2).ToString(); } + + // For unit testing + public void SetFakeFPSValue(double fps) + { + SetValue(fps); + } } diff --git a/tests/Highbyte.DotNet6502.Tests/Instrumentation/ElapsedMillisecondsTimedStatTest.cs b/tests/Highbyte.DotNet6502.Tests/Instrumentation/ElapsedMillisecondsTimedStatTest.cs index 044772a5..17734238 100644 --- a/tests/Highbyte.DotNet6502.Tests/Instrumentation/ElapsedMillisecondsTimedStatTest.cs +++ b/tests/Highbyte.DotNet6502.Tests/Instrumentation/ElapsedMillisecondsTimedStatTest.cs @@ -56,5 +56,46 @@ public void Measure_WhenUsed_With_Cont_Returns_ExecutionTime_In_GetStatMilliseco Assert.True(elapsedMs < (sleepMs + sleepNextMs) + 10); #endif } + + [Fact] + public void GetDescription_WhenUsed_Returns_Null_When_No_Data_Yet() + { + // Arrange + var stat = new ElapsedMillisecondsTimedStat(samples: 1); + + // Act + // Assert + Assert.Equal("null", stat.GetDescription()); + } + + [Fact] + public void GetDescription_WhenUsed_Returns_Special_String_When_Duration_Is_Less_Than_OneHundreds_Of_A_Millisecond() + { + // Arrange + var stat = new ElapsedMillisecondsTimedStat(samples: 1); + + // Act + stat.SetFakeMSValue(0.0099); + + // Assert + Assert.Equal("< 0.01ms", stat.GetDescription()); + } + + [Fact] + public void GetDescription_WhenUsed_Returns_String_With_Milliseconds() + { + // Arrange + var stat = new ElapsedMillisecondsTimedStat(samples: 1); + + // Act + int sleepMs = 2; + using (stat.Measure()) + { + Thread.Sleep(sleepMs); + } + // Assert + var ms = stat.GetStatMilliseconds(); + Assert.Equal($"{Math.Round(ms.Value, 2).ToString("0.00")}ms", stat.GetDescription()); + } } } diff --git a/tests/Highbyte.DotNet6502.Tests/Instrumentation/PerSecondTimedStatTest.cs b/tests/Highbyte.DotNet6502.Tests/Instrumentation/PerSecondTimedStatTest.cs index bf2f7b9d..05994418 100644 --- a/tests/Highbyte.DotNet6502.Tests/Instrumentation/PerSecondTimedStatTest.cs +++ b/tests/Highbyte.DotNet6502.Tests/Instrumentation/PerSecondTimedStatTest.cs @@ -1,4 +1,5 @@ using Highbyte.DotNet6502.Instrumentation.Stats; +using Newtonsoft.Json.Linq; namespace Highbyte.DotNet6502.Tests.Instrumentation { @@ -23,5 +24,45 @@ public void Update_WhenUsed_Returns_Correct_PerSecond_Value() Assert.True(perSecond >= 55); Assert.True(perSecond < 65); } + + [Fact] + public void GetDescription_WhenUsed_Returns_Null_When_No_Data_Yet() + { + // Arrange + var stat = new PerSecondTimedStat(); + + // Act + // Assert + Assert.Equal("null", stat.GetDescription()); + } + + [Fact] + public void GetDescription_WhenUsed_Returns_Special_String_When_FPS_Is_Less_Than_OneHundreds_Of_A_Second() + { + // Arrange + var stat = new PerSecondTimedStat(); + + // Act + stat.SetFakeFPSValue(0.009); + + // Assert + Assert.Equal("< 0.01", stat.GetDescription()); + } + + [Fact] + public void GetDescription_WhenUsed_Returns_String_With_FPS() + { + // Arrange + var stat = new PerSecondTimedStat(); + + // Act + stat.Update(); + Thread.Sleep(16); + stat.Update(); + + // Assert + var fps = stat.Value; + Assert.Equal(Math.Round(fps ?? 0, 2).ToString(), stat.GetDescription()); + } } } From 89e4464fec955f3e9318521e3955d8b49feb8276 Mon Sep 17 00:00:00 2001 From: Highbyte Date: Wed, 29 Nov 2023 17:29:43 +0100 Subject: [PATCH 10/10] Fix test --- .../Instrumentation/PerSecondTimedStatTest.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/tests/Highbyte.DotNet6502.Tests/Instrumentation/PerSecondTimedStatTest.cs b/tests/Highbyte.DotNet6502.Tests/Instrumentation/PerSecondTimedStatTest.cs index 05994418..d11f0bbc 100644 --- a/tests/Highbyte.DotNet6502.Tests/Instrumentation/PerSecondTimedStatTest.cs +++ b/tests/Highbyte.DotNet6502.Tests/Instrumentation/PerSecondTimedStatTest.cs @@ -14,15 +14,10 @@ public void Update_WhenUsed_Returns_Correct_PerSecond_Value() var stat = new PerSecondTimedStat(); // Act - stat.Update(); - int sleepMs = 16; // 16 ms should give arround 60 FPS - Thread.Sleep(sleepMs); - stat.Update(); + stat.SetFakeFPSValue(60); // Assert - var perSecond = stat.Value; - Assert.True(perSecond >= 55); - Assert.True(perSecond < 65); + Assert.Equal(60, stat.Value); } [Fact]