Browse Source

Prototype Scenario benchmark

Tig 9 months ago
parent
commit
69cd30cc19

+ 1 - 1
Terminal.Gui/Clipboard/Clipboard.cs

@@ -148,7 +148,7 @@ internal static class ClipboardProcessRunner
         bool waitForOutput = true
     )
     {
-        var output = string.Empty;
+            var output = string.Empty;
 
         using (var process = new Process
                {

+ 8 - 8
UICatalog/Properties/launchSettings.json

@@ -27,14 +27,6 @@
       "commandName": "Project",
       "commandLineArgs": "Sliders"
     },
-    "Wizards": {
-      "commandName": "Project",
-      "commandLineArgs": "Wizards"
-    },
-    "Dialogs": {
-      "commandName": "Project",
-      "commandLineArgs": "Dialogs"
-    },
     "Buttons": {
       "commandName": "Project",
       "commandLineArgs": "Buttons"
@@ -73,6 +65,14 @@
     "Frames Demo": {
       "commandName": "Project",
       "commandLineArgs": "\"Frames Demo\""
+    },
+    "Generic": {
+      "commandName": "Project",
+      "commandLineArgs": "Generic"
+    },
+    "Arrangement": {
+      "commandName": "Project",
+      "commandLineArgs": "Arrangement"
     }
   }
 }

+ 105 - 0
UICatalog/Scenarios/Benchmark.cs

@@ -0,0 +1,105 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+using System.Linq;
+using System.Reflection;
+using System.Threading.Tasks;
+using Terminal.Gui;
+
+namespace UICatalog.Scenarios;
+
+[ScenarioMetadata ("Benchmark", "Benchmarks Terminal.Gui Layout and Draw Perf.")]
+public sealed class Benchmark : Scenario
+{
+    public override void Main ()
+    {
+        // Init
+        Application.Init ();
+
+        // Setup - Create a top-level application window and configure it.
+        Window appWindow = new ()
+        {
+            Title = GetQuitKeyAndName (),
+        };
+
+        ListView scenarioList = new ListView ()
+        {
+            Title = "_Sceanrios",
+            BorderStyle = LineStyle.Rounded,
+            Width = Dim.Auto (),
+            Height = Dim.Fill (),
+        };
+
+        var types = AllScenarioTypes;
+        ObservableCollection<string> scenarios = new ObservableCollection<string> (AllScenarioTypes.Select (
+                                                                                                       t =>
+                                                                                                       {
+                                                                                                           var attr = t.GetCustomAttributes (
+                                                                                                                    typeof (ScenarioMetadata),
+                                                                                                                    false) [0] as ScenarioMetadata;
+
+                                                                                                           return attr.Name;
+
+                                                                                                       }));
+        scenarioList.Source = new ListWrapper<string> (scenarios);
+
+        scenarioList.Accepting += (sender, args) =>
+                                  {
+
+                                      bool waitForOutput = true;
+                                      var output = string.Empty;
+
+
+
+                                      //Task.Run (
+                                      //  () =>
+                                      //{
+                                      using var process = new Process
+                                      {
+                                          StartInfo = new ()
+                                          {
+                                              FileName = "UICatalog.exe",
+                                              Arguments = $"{scenarios [scenarioList.SelectedItem]}",
+                                              RedirectStandardOutput = false,
+                                              RedirectStandardError = false,
+                                              RedirectStandardInput = false,
+                                              UseShellExecute = true,
+                                              CreateNoWindow = true
+                                          }
+                                      };
+
+                                      process.Start ();
+
+                                      if (!process.WaitForExit (10000))
+                                      {
+                                          var timeoutError =
+                                              $@"Process timed out. Command line: {process.StartInfo.FileName} {process.StartInfo.Arguments}.";
+
+                                          Debug.WriteLine (timeoutError);
+
+                                          process.Close ();
+
+                                          return;
+                                      }
+                                      //   });
+
+                                  };
+
+        appWindow.Add (scenarioList);
+
+        // Run - Start the application.
+        Application.Run (appWindow);
+        appWindow.Dispose ();
+
+        // Shutdown - Calling Application.Shutdown is required.
+        Application.Shutdown ();
+    }
+
+    public static IEnumerable<Type> AllScenarioTypes =>
+        typeof (Scenario).Assembly
+                         .GetTypes ()
+                         .Where (type => type.IsClass && !type.IsAbstract && type.IsSubclassOf (typeof (Scenario)))
+                         .Select (type => type);
+
+}

+ 0 - 0
UICatalog/Resources/config.json → UICatalog/Scenarios/Editors/Resources/config.json


+ 0 - 3
UICatalog/Scenarios/Generic.cs

@@ -19,9 +19,6 @@ public sealed class Generic : Scenario
 
         var button = new Button { Id = "button", X = Pos.Center (), Y = 1, Text = "_Press me!" };
 
-        button.ShadowStyle = ShadowStyle.None;
-        button.HighlightStyle = HighlightStyle.None;
-
         button.Accepting += (s, e) => MessageBox.ErrorQuery ("Error", "You pressed the button!", "_Ok");
         appWindow.Add (button);
 

+ 139 - 7
UICatalog/UICatalog.cs

@@ -294,6 +294,7 @@ public class UICatalogApp
         _homeDirWatcher.Created -= ConfigFileChanged;
     }
 
+
     private static void UICatalogMain (Options options)
     {
         StartConfigFileWatcher ();
@@ -316,14 +317,8 @@ public class UICatalogApp
                                                                        )!);
             _selectedScenario = (Scenario)Activator.CreateInstance (_scenarios [item].GetType ())!;
 
-            Application.Init (driverName: _forceDriver);
-            _selectedScenario.TopLevelColorScheme = _topLevelColorScheme;
-            _selectedScenario.Main ();
-            _selectedScenario.Dispose ();
-            _selectedScenario = null;
+            BenchmarkScenario ();
 
-            // TODO: Throw if shutdown was not called already
-            Application.Shutdown ();
             VerifyObjectsWereDisposed ();
 
             return;
@@ -378,6 +373,143 @@ public class UICatalogApp
 
     }
 
+    private static void BenchmarkScenario ()
+    {
+        object _timeoutLock = new ();
+
+        uint maxIterations = 1000;
+        uint abortTime = 5000;
+        object timeout = null;
+        var initialized = false;
+        var shutdown = false;
+
+        int iterationCount = 0;
+        int clearedContentCount = 0;
+        int refreshedCount = 0;
+        int updatedCount = 0;
+        int drawCompleteCount = 0;
+
+        int laidOutCount = 0;
+
+        Console.WriteLine ($"Scenario {_selectedScenario.GetName ()}");
+
+        Stopwatch stopwatch = null;
+
+        Application.InitializedChanged += OnApplicationOnInitializedChanged;
+
+        Application.Init (driverName: _forceDriver);
+        _selectedScenario.TopLevelColorScheme = _topLevelColorScheme;
+
+        _selectedScenario.Main ();
+
+        Application.InitializedChanged -= OnApplicationOnInitializedChanged;
+
+        Console.WriteLine ($"  ran for {iterationCount} iterations.");
+        Console.WriteLine ($"  took {stopwatch?.ElapsedMilliseconds} ms to run.");
+        Console.WriteLine ($"  called Driver.ClearContents {clearedContentCount} times.");
+        Console.WriteLine ($"  called Driver.Refresh {refreshedCount} times.");
+        Console.WriteLine ($"    which updated the screen {updatedCount} times.");
+        Console.WriteLine ($"  called View.Draw {drawCompleteCount} times.");
+        Console.WriteLine ($"  called View.LayoutComplete {laidOutCount} times.");
+
+        _selectedScenario.Dispose ();
+        _selectedScenario = null;
+
+        lock (_timeoutLock)
+        {
+            if (timeout is { })
+            {
+                timeout = null;
+            }
+        }
+
+        // TODO: Throw if shutdown was not called already
+        Application.Shutdown ();
+
+        return;
+
+
+        void OnApplicationOnInitializedChanged (object s, EventArgs<bool> a)
+        {
+            if (a.CurrentValue)
+            {
+                lock (_timeoutLock)
+                {
+                    timeout = Application.AddTimeout (TimeSpan.FromMilliseconds (abortTime), ForceCloseCallback);
+                }
+
+                initialized = true;
+                Application.Iteration += OnApplicationOnIteration;
+                Application.Driver!.ClearedContents += (sender, args) => clearedContentCount++;
+                Application.Driver!.Refreshed += (sender, args) =>
+                {
+                    refreshedCount++;
+
+                    if (args.CurrentValue)
+                    {
+                        updatedCount++;
+                    }
+                };
+                Application.NotifyNewRunState += OnApplicationNotifyNewRunState;
+
+                stopwatch = Stopwatch.StartNew ();
+            }
+            else
+            {
+                shutdown = true;
+                Application.NotifyNewRunState -= OnApplicationNotifyNewRunState;
+                Application.Iteration -= OnApplicationOnIteration;
+                stopwatch?.Stop ();
+            }
+        }
+
+        void OnApplicationOnIteration (object s, IterationEventArgs a)
+        {
+            iterationCount++;
+            if (iterationCount > maxIterations)
+            {
+                Application.RequestStop ();
+            }
+        }
+
+
+        void OnApplicationNotifyNewRunState (object sender, RunStateEventArgs e)
+        {
+            // Get a list of all subviews under Application.Top (and their subviews, etc.)
+            // and subscribe to their DrawComplete event
+            void SubscribeAllSubviews (View view)
+            {
+                view.DrawComplete += (s, a) => drawCompleteCount++;
+                view.SubviewsLaidOut += (s, a) => laidOutCount++;
+                foreach (View subview in view.Subviews)
+                {
+                    SubscribeAllSubviews (subview);
+                }
+            }
+
+            SubscribeAllSubviews (Application.Top);
+        }
+
+        // If the scenario doesn't close within the abort time, this will force it to quit
+        bool ForceCloseCallback ()
+        {
+            lock (_timeoutLock)
+            {
+                if (timeout is { })
+                {
+                    timeout = null;
+                }
+            }
+
+            Console.WriteLine ($"  Failed to Quit with {Application.QuitKey} after {abortTime}ms and {iterationCount} iterations. Force quit.");
+
+            Application.RequestStop ();
+
+            return false;
+        }
+
+    }
+
     private static void VerifyObjectsWereDisposed ()
     {
 #if DEBUG_IDISPOSABLE

+ 2 - 2
UICatalog/UICatalog.csproj

@@ -20,10 +20,10 @@
     <DefineConstants>TRACE;DEBUG_IDISPOSABLE</DefineConstants>
   </PropertyGroup>
   <ItemGroup>
-    <None Remove="Resources\config.json" />
+    <None Remove="Scenarios\Editors\Resources\config.json" />
   </ItemGroup>
   <ItemGroup>
-    <EmbeddedResource Include="Resources\config.json" />
+    <EmbeddedResource Include="Scenarios\Editors\Resources\config.json" />
   </ItemGroup>
   <ItemGroup>
   <None Update="Scenarios\AnimationScenario\Spinning_globe_dark_small.gif" CopyToOutputDirectory="PreserveNewest" />