diff --git a/ARMeilleure/ARMeilleure.csproj b/ARMeilleure/ARMeilleure.csproj
index 4f55243fed..9567838ecc 100644
--- a/ARMeilleure/ARMeilleure.csproj
+++ b/ARMeilleure/ARMeilleure.csproj
@@ -13,6 +13,15 @@
true
+
+ true
+ true
+
+
+
+ true
+
+
diff --git a/Ryujinx.Audio/Ryujinx.Audio.csproj b/Ryujinx.Audio/Ryujinx.Audio.csproj
index 588b691814..b541043c75 100644
--- a/Ryujinx.Audio/Ryujinx.Audio.csproj
+++ b/Ryujinx.Audio/Ryujinx.Audio.csproj
@@ -12,7 +12,7 @@
true
- TRACE;USE_PROFILING
+ TRACE;USE_DEBUGGING
false
@@ -22,7 +22,7 @@
true
- TRACE;USE_PROFILING
+ TRACE;USE_DEBUGGING
true
diff --git a/Ryujinx.Common/Ryujinx.Common.csproj b/Ryujinx.Common/Ryujinx.Common.csproj
index 7f6fa32323..43a853a409 100644
--- a/Ryujinx.Common/Ryujinx.Common.csproj
+++ b/Ryujinx.Common/Ryujinx.Common.csproj
@@ -12,7 +12,7 @@
true
- TRACE;USE_PROFILING
+ TRACE;USE_DEBUGGING
false
@@ -22,7 +22,7 @@
true
- TRACE;USE_PROFILING
+ TRACE;USE_DEBUGGING
true
diff --git a/Ryujinx.Debugger/Debugger.cs b/Ryujinx.Debugger/Debugger.cs
new file mode 100644
index 0000000000..6dd3354ce9
--- /dev/null
+++ b/Ryujinx.Debugger/Debugger.cs
@@ -0,0 +1,32 @@
+using System;
+using Ryujinx.Debugger.UI;
+
+namespace Ryujinx.Debugger
+{
+ public class Debugger : IDisposable
+ {
+ public DebuggerWidget Widget { get; set; }
+
+ public Debugger()
+ {
+ Widget = new DebuggerWidget();
+ }
+
+ public void Enable()
+ {
+ Widget.Enable();
+ }
+
+ public void Disable()
+ {
+ Widget.Disable();
+ }
+
+ public void Dispose()
+ {
+ Disable();
+
+ Widget.Dispose();
+ }
+ }
+}
diff --git a/Ryujinx.Profiler/DumpProfile.cs b/Ryujinx.Debugger/Profiler/DumpProfile.cs
similarity index 97%
rename from Ryujinx.Profiler/DumpProfile.cs
rename to Ryujinx.Debugger/Profiler/DumpProfile.cs
index 62a027615d..e73314d4a7 100644
--- a/Ryujinx.Profiler/DumpProfile.cs
+++ b/Ryujinx.Debugger/Profiler/DumpProfile.cs
@@ -4,7 +4,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
-namespace Ryujinx.Profiler
+namespace Ryujinx.Debugger.Profiler
{
public static class DumpProfile
{
diff --git a/Ryujinx.Profiler/InternalProfile.cs b/Ryujinx.Debugger/Profiler/InternalProfile.cs
similarity index 93%
rename from Ryujinx.Profiler/InternalProfile.cs
rename to Ryujinx.Debugger/Profiler/InternalProfile.cs
index 0346244423..0bda9e049b 100644
--- a/Ryujinx.Profiler/InternalProfile.cs
+++ b/Ryujinx.Debugger/Profiler/InternalProfile.cs
@@ -1,12 +1,12 @@
-using System;
+using Ryujinx.Common;
+using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using Ryujinx.Common;
-namespace Ryujinx.Profiler
+namespace Ryujinx.Debugger.Profiler
{
public class InternalProfile
{
@@ -26,17 +26,17 @@ namespace Ryujinx.Profiler
// Cleanup thread
private readonly Thread _cleanupThread;
- private bool _cleanupRunning;
- private readonly long _history;
- private long _preserve;
+ private bool _cleanupRunning;
+ private readonly long _history;
+ private long _preserve;
// Timing flags
private TimingFlag[] _timingFlags;
- private long[] _timingFlagAverages;
- private long[] _timingFlagLast;
- private long[] _timingFlagLastDelta;
- private int _timingFlagCount;
- private int _timingFlagIndex;
+ private long[] _timingFlagAverages;
+ private long[] _timingFlagLast;
+ private long[] _timingFlagLastDelta;
+ private int _timingFlagCount;
+ private int _timingFlagIndex;
private int _maxFlags;
diff --git a/Ryujinx.Profiler/Profile.cs b/Ryujinx.Debugger/Profiler/Profile.cs
similarity index 88%
rename from Ryujinx.Profiler/Profile.cs
rename to Ryujinx.Debugger/Profiler/Profile.cs
index 4dba6ea542..862aa845d1 100644
--- a/Ryujinx.Profiler/Profile.cs
+++ b/Ryujinx.Debugger/Profiler/Profile.cs
@@ -4,19 +4,17 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
-namespace Ryujinx.Profiler
+namespace Ryujinx.Debugger.Profiler
{
public static class Profile
{
public static float UpdateRate => _settings.UpdateRate;
public static long HistoryLength => _settings.History;
- public static ProfilerKeyboardHandler Controls => _settings.Controls;
-
private static InternalProfile _profileInstance;
private static ProfilerSettings _settings;
- [Conditional("USE_PROFILING")]
+ [Conditional("USE_DEBUGGING")]
public static void Initialize()
{
var config = ProfilerConfiguration.Load(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ProfilerConfig.jsonc"));
@@ -29,14 +27,13 @@ namespace Ryujinx.Profiler
UpdateRate = (config.UpdateRate <= 0) ? -1 : 1.0f / config.UpdateRate,
History = (long)(config.History * PerformanceCounter.TicksPerSecond),
MaxLevel = config.MaxLevel,
- Controls = config.Controls,
MaxFlags = config.MaxFlags,
};
}
public static bool ProfilingEnabled()
{
-#if USE_PROFILING
+#if USE_DEBUGGING
if (!_settings.Enabled)
return false;
@@ -49,7 +46,7 @@ namespace Ryujinx.Profiler
#endif
}
- [Conditional("USE_PROFILING")]
+ [Conditional("USE_DEBUGGING")]
public static void FinishProfiling()
{
if (!ProfilingEnabled())
@@ -61,7 +58,7 @@ namespace Ryujinx.Profiler
_profileInstance.Dispose();
}
- [Conditional("USE_PROFILING")]
+ [Conditional("USE_DEBUGGING")]
public static void FlagTime(TimingFlagType flagType)
{
if (!ProfilingEnabled())
@@ -69,7 +66,7 @@ namespace Ryujinx.Profiler
_profileInstance.FlagTime(flagType);
}
- [Conditional("USE_PROFILING")]
+ [Conditional("USE_DEBUGGING")]
public static void RegisterFlagReceiver(Action receiver)
{
if (!ProfilingEnabled())
@@ -77,7 +74,7 @@ namespace Ryujinx.Profiler
_profileInstance.RegisterFlagReceiver(receiver);
}
- [Conditional("USE_PROFILING")]
+ [Conditional("USE_DEBUGGING")]
public static void Begin(ProfileConfig config)
{
if (!ProfilingEnabled())
@@ -87,7 +84,7 @@ namespace Ryujinx.Profiler
_profileInstance.BeginProfile(config);
}
- [Conditional("USE_PROFILING")]
+ [Conditional("USE_DEBUGGING")]
public static void End(ProfileConfig config)
{
if (!ProfilingEnabled())
@@ -99,7 +96,7 @@ namespace Ryujinx.Profiler
public static string GetSession()
{
-#if USE_PROFILING
+#if USE_DEBUGGING
if (!ProfilingEnabled())
return null;
return _profileInstance.GetSession();
@@ -110,7 +107,7 @@ namespace Ryujinx.Profiler
public static List> GetProfilingData()
{
-#if USE_PROFILING
+#if USE_DEBUGGING
if (!ProfilingEnabled())
return new List>();
return _profileInstance.GetProfilingData();
@@ -121,7 +118,7 @@ namespace Ryujinx.Profiler
public static TimingFlag[] GetTimingFlags()
{
-#if USE_PROFILING
+#if USE_DEBUGGING
if (!ProfilingEnabled())
return new TimingFlag[0];
return _profileInstance.GetTimingFlags();
@@ -132,7 +129,7 @@ namespace Ryujinx.Profiler
public static (long[], long[]) GetTimingAveragesAndLast()
{
-#if USE_PROFILING
+#if USE_DEBUGGING
if (!ProfilingEnabled())
return (new long[0], new long[0]);
return _profileInstance.GetTimingAveragesAndLast();
diff --git a/Ryujinx.Profiler/ProfileConfig.cs b/Ryujinx.Debugger/Profiler/ProfileConfig.cs
similarity index 99%
rename from Ryujinx.Profiler/ProfileConfig.cs
rename to Ryujinx.Debugger/Profiler/ProfileConfig.cs
index 4271bd2b86..0ec3e26db2 100644
--- a/Ryujinx.Profiler/ProfileConfig.cs
+++ b/Ryujinx.Debugger/Profiler/ProfileConfig.cs
@@ -1,6 +1,6 @@
using System;
-namespace Ryujinx.Profiler
+namespace Ryujinx.Debugger.Profiler
{
public struct ProfileConfig : IEquatable
{
diff --git a/Ryujinx.Profiler/UI/ProfileSorters.cs b/Ryujinx.Debugger/Profiler/ProfileSorters.cs
similarity index 97%
rename from Ryujinx.Profiler/UI/ProfileSorters.cs
rename to Ryujinx.Debugger/Profiler/ProfileSorters.cs
index 9f66de224c..2b730af58a 100644
--- a/Ryujinx.Profiler/UI/ProfileSorters.cs
+++ b/Ryujinx.Debugger/Profiler/ProfileSorters.cs
@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
-namespace Ryujinx.Profiler.UI
+namespace Ryujinx.Debugger.Profiler
{
public static class ProfileSorters
{
diff --git a/Ryujinx.Profiler/ProfilerConfiguration.cs b/Ryujinx.Debugger/Profiler/ProfilerConfiguration.cs
similarity index 94%
rename from Ryujinx.Profiler/ProfilerConfiguration.cs
rename to Ryujinx.Debugger/Profiler/ProfilerConfiguration.cs
index 4fe616fa99..e0842f2e1f 100644
--- a/Ryujinx.Profiler/ProfilerConfiguration.cs
+++ b/Ryujinx.Debugger/Profiler/ProfilerConfiguration.cs
@@ -1,10 +1,10 @@
-using OpenTK.Input;
+using Gdk;
using System;
using System.IO;
using Utf8Json;
using Utf8Json.Resolvers;
-namespace Ryujinx.Profiler
+namespace Ryujinx.Debugger.Profiler
{
public class ProfilerConfiguration
{
@@ -15,8 +15,6 @@ namespace Ryujinx.Profiler
public int MaxFlags { get; private set; }
public float History { get; private set; }
- public ProfilerKeyboardHandler Controls { get; private set; }
-
///
/// Loads a configuration file from disk
///
diff --git a/Ryujinx.Profiler/Settings.cs b/Ryujinx.Debugger/Profiler/Settings.cs
similarity index 86%
rename from Ryujinx.Profiler/Settings.cs
rename to Ryujinx.Debugger/Profiler/Settings.cs
index f0c851b229..52aa0d8427 100644
--- a/Ryujinx.Profiler/Settings.cs
+++ b/Ryujinx.Debugger/Profiler/Settings.cs
@@ -1,4 +1,4 @@
-namespace Ryujinx.Profiler
+namespace Ryujinx.Debugger.Profiler
{
public class ProfilerSettings
{
@@ -13,8 +13,5 @@
// 19531225 = 5 seconds in ticks on most pc's.
// It should get set on boot to the time specified in config
public long History { get; set; } = 19531225;
-
- // Controls
- public ProfilerKeyboardHandler Controls;
}
}
diff --git a/Ryujinx.Profiler/TimingFlag.cs b/Ryujinx.Debugger/Profiler/TimingFlag.cs
similarity index 87%
rename from Ryujinx.Profiler/TimingFlag.cs
rename to Ryujinx.Debugger/Profiler/TimingFlag.cs
index 0cf55bdf32..8a34ac99f3 100644
--- a/Ryujinx.Profiler/TimingFlag.cs
+++ b/Ryujinx.Debugger/Profiler/TimingFlag.cs
@@ -1,4 +1,4 @@
-namespace Ryujinx.Profiler
+namespace Ryujinx.Debugger.Profiler
{
public enum TimingFlagType
{
diff --git a/Ryujinx.Profiler/TimingInfo.cs b/Ryujinx.Debugger/Profiler/TimingInfo.cs
similarity index 99%
rename from Ryujinx.Profiler/TimingInfo.cs
rename to Ryujinx.Debugger/Profiler/TimingInfo.cs
index 6058ddbd81..90bd63d291 100644
--- a/Ryujinx.Profiler/TimingInfo.cs
+++ b/Ryujinx.Debugger/Profiler/TimingInfo.cs
@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
-namespace Ryujinx.Profiler
+namespace Ryujinx.Debugger.Profiler
{
public struct Timestamp
{
diff --git a/Ryujinx.Profiler/ProfilerConfig.jsonc b/Ryujinx.Debugger/ProfilerConfig.jsonc
similarity index 100%
rename from Ryujinx.Profiler/ProfilerConfig.jsonc
rename to Ryujinx.Debugger/ProfilerConfig.jsonc
diff --git a/Ryujinx.Debugger/Ryujinx.Debugger.csproj b/Ryujinx.Debugger/Ryujinx.Debugger.csproj
new file mode 100644
index 0000000000..a67662cc03
--- /dev/null
+++ b/Ryujinx.Debugger/Ryujinx.Debugger.csproj
@@ -0,0 +1,42 @@
+
+
+
+ netcoreapp3.0
+ Debug;Release;Profile Release;Profile Debug
+
+
+
+ TRACE;USE_DEBUGGING
+
+
+
+ TRACE;USE_DEBUGGING
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
diff --git a/Ryujinx.Debugger/UI/DebuggerWidget.cs b/Ryujinx.Debugger/UI/DebuggerWidget.cs
new file mode 100644
index 0000000000..b2d8458dc9
--- /dev/null
+++ b/Ryujinx.Debugger/UI/DebuggerWidget.cs
@@ -0,0 +1,42 @@
+using Gtk;
+using System;
+using GUI = Gtk.Builder.ObjectAttribute;
+
+namespace Ryujinx.Debugger.UI
+{
+ public class DebuggerWidget : Box
+ {
+ public event EventHandler DebuggerEnabled;
+ public event EventHandler DebuggerDisabled;
+
+ [GUI] Notebook _widgetNotebook;
+
+ public DebuggerWidget() : this(new Builder("Ryujinx.Debugger.UI.DebuggerWidget.glade")) { }
+
+ public DebuggerWidget(Builder builder) : base(builder.GetObject("_debuggerBox").Handle)
+ {
+ builder.Autoconnect(this);
+
+ LoadProfiler();
+ }
+
+ public void LoadProfiler()
+ {
+ ProfilerWidget widget = new ProfilerWidget();
+
+ widget.RegisterParentDebugger(this);
+
+ _widgetNotebook.AppendPage(widget, new Label("Profiler"));
+ }
+
+ public void Enable()
+ {
+ DebuggerEnabled.Invoke(this, null);
+ }
+
+ public void Disable()
+ {
+ DebuggerDisabled.Invoke(this, null);
+ }
+ }
+}
diff --git a/Ryujinx.Debugger/UI/DebuggerWidget.glade b/Ryujinx.Debugger/UI/DebuggerWidget.glade
new file mode 100644
index 0000000000..7e6e691de1
--- /dev/null
+++ b/Ryujinx.Debugger/UI/DebuggerWidget.glade
@@ -0,0 +1,44 @@
+
+
+
+
+
+
diff --git a/Ryujinx.Debugger/UI/ProfilerWidget.cs b/Ryujinx.Debugger/UI/ProfilerWidget.cs
new file mode 100644
index 0000000000..0dc4b84f42
--- /dev/null
+++ b/Ryujinx.Debugger/UI/ProfilerWidget.cs
@@ -0,0 +1,801 @@
+using Gtk;
+using Ryujinx.Common;
+using Ryujinx.Debugger.Profiler;
+using SkiaSharp;
+using SkiaSharp.Views.Desktop;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text.RegularExpressions;
+using System.Threading;
+
+using GUI = Gtk.Builder.ObjectAttribute;
+
+namespace Ryujinx.Debugger.UI
+{
+ public class ProfilerWidget : Box
+ {
+ private Thread _profilerThread;
+ private double _prevTime;
+ private bool _profilerRunning;
+
+ private TimingFlag[] _timingFlags;
+
+ private bool _initComplete = false;
+ private bool _redrawPending = true;
+ private bool _doStep = false;
+
+ // Layout
+ private const int LineHeight = 16;
+ private const int MinimumColumnWidth = 200;
+ private const int TitleHeight = 24;
+ private const int TitleFontHeight = 16;
+ private const int LinePadding = 2;
+ private const int ColumnSpacing = 15;
+ private const int FilterHeight = 24;
+ private const int BottomBarHeight = FilterHeight + LineHeight;
+
+ // Sorting
+ private List> _unsortedProfileData;
+ private IComparer> _sortAction = new ProfileSorters.TagAscending();
+
+ // Flag data
+ private long[] _timingFlagsAverages;
+ private long[] _timingFlagsLast;
+
+ // Filtering
+ private string _filterText = "";
+ private bool _regexEnabled = false;
+
+ // Scrolling
+ private float _scrollPos = 0;
+
+ // Profile data storage
+ private List> _sortedProfileData;
+ private long _captureTime;
+
+ // Graph
+ private SKColor[] _timingFlagColors = new[]
+ {
+ new SKColor(150, 25, 25, 50), // FrameSwap = 0
+ new SKColor(25, 25, 150, 50), // SystemFrame = 1
+ };
+
+ private const float GraphMoveSpeed = 40000;
+ private const float GraphZoomSpeed = 50;
+
+ private float _graphZoom = 1;
+ private float _graphPosition = 0;
+ private int _rendererHeight => _renderer.AllocatedHeight;
+ private int _rendererWidth => _renderer.AllocatedWidth;
+
+ // Event management
+ private long _lastOutputUpdate;
+ private long _lastOutputDraw;
+ private long _lastOutputUpdateDuration;
+ private long _lastOutputDrawDuration;
+ private double _lastFrameTimeMs;
+ private double _updateTimer;
+ private bool _profileUpdated = false;
+ private readonly object _profileDataLock = new object();
+
+ private SkRenderer _renderer;
+
+ [GUI] ScrolledWindow _scrollview;
+ [GUI] CheckButton _enableCheckbutton;
+ [GUI] Scrollbar _outputScrollbar;
+ [GUI] Entry _filterBox;
+ [GUI] ComboBox _modeBox;
+ [GUI] CheckButton _showFlags;
+ [GUI] CheckButton _showInactive;
+ [GUI] Button _stepButton;
+ [GUI] CheckButton _pauseCheckbutton;
+
+ public ProfilerWidget() : this(new Builder("Ryujinx.Debugger.UI.ProfilerWidget.glade")) { }
+
+ public ProfilerWidget(Builder builder) : base(builder.GetObject("_profilerBox").Handle)
+ {
+ builder.Autoconnect(this);
+
+ this.KeyPressEvent += ProfilerWidget_KeyPressEvent;
+
+ this.Expand = true;
+
+ _renderer = new SkRenderer();
+ _renderer.Expand = true;
+
+ _outputScrollbar.ValueChanged += _outputScrollbar_ValueChanged;
+
+ _renderer.DrawGraphs += _renderer_DrawGraphs;
+
+ _filterBox.Changed += _filterBox_Changed;
+
+ _stepButton.Clicked += _stepButton_Clicked;
+
+ _scrollview.Add(_renderer);
+
+ if (Profile.UpdateRate <= 0)
+ {
+ // Perform step regardless of flag type
+ Profile.RegisterFlagReceiver((t) =>
+ {
+ if (_pauseCheckbutton.Active)
+ {
+ _doStep = true;
+ }
+ });
+ }
+ }
+
+ private void _stepButton_Clicked(object sender, EventArgs e)
+ {
+ if (_pauseCheckbutton.Active)
+ {
+ _doStep = true;
+ }
+
+ _profileUpdated = true;
+ }
+
+ private void _filterBox_Changed(object sender, EventArgs e)
+ {
+ _filterText = _filterBox.Text;
+ _profileUpdated = true;
+ }
+
+ private void _outputScrollbar_ValueChanged(object sender, EventArgs e)
+ {
+ _scrollPos = -(float)Math.Max(0, _outputScrollbar.Value);
+ _profileUpdated = true;
+ }
+
+ private void _renderer_DrawGraphs(object sender, EventArgs e)
+ {
+ if (e is SKPaintSurfaceEventArgs se)
+ {
+ Draw(se.Surface.Canvas);
+ }
+ }
+
+ public void RegisterParentDebugger(DebuggerWidget debugger)
+ {
+ debugger.DebuggerEnabled += Debugger_DebuggerAttached;
+ debugger.DebuggerDisabled += Debugger_DebuggerDettached;
+ }
+
+ private void Debugger_DebuggerDettached(object sender, EventArgs e)
+ {
+ _profilerRunning = false;
+
+ if (_profilerThread != null)
+ {
+ _profilerThread.Join();
+ }
+ }
+
+ private void Debugger_DebuggerAttached(object sender, EventArgs e)
+ {
+ _profilerRunning = false;
+
+ if (_profilerThread != null)
+ {
+ _profilerThread.Join();
+ }
+
+ _profilerRunning = true;
+
+ _profilerThread = new Thread(UpdateLoop)
+ {
+ Name = "Profiler.UpdateThread"
+ };
+ _profilerThread.Start();
+ }
+
+ private void ProfilerWidget_KeyPressEvent(object o, Gtk.KeyPressEventArgs args)
+ {
+ switch (args.Event.Key)
+ {
+ case Gdk.Key.Left:
+ _graphPosition += (long)(GraphMoveSpeed * _lastFrameTimeMs);
+ break;
+
+ case Gdk.Key.Right:
+ _graphPosition = Math.Max(_graphPosition - (long)(GraphMoveSpeed * _lastFrameTimeMs), 0);
+ break;
+
+ case Gdk.Key.Up:
+ _graphZoom = MathF.Min(_graphZoom + (float)(GraphZoomSpeed * _lastFrameTimeMs), 100.0f);
+ break;
+
+ case Gdk.Key.Down:
+ _graphZoom = MathF.Max(_graphZoom - (float)(GraphZoomSpeed * _lastFrameTimeMs), 1f);
+ break;
+ }
+ _profileUpdated = true;
+ }
+
+ public void UpdateLoop()
+ {
+ _lastOutputUpdate = PerformanceCounter.ElapsedTicks;
+ _lastOutputDraw = PerformanceCounter.ElapsedTicks;
+
+ while (_profilerRunning)
+ {
+ _lastOutputUpdate = PerformanceCounter.ElapsedTicks;
+ int timeToSleepMs = (_pauseCheckbutton.Active || !_enableCheckbutton.Active) ? 33 : 1;
+
+ if (Profile.ProfilingEnabled() && _enableCheckbutton.Active)
+ {
+ double time = (double)PerformanceCounter.ElapsedTicks / PerformanceCounter.TicksPerSecond;
+
+ Update(time - _prevTime);
+
+ _lastOutputUpdateDuration = PerformanceCounter.ElapsedTicks - _lastOutputUpdate;
+ _prevTime = time;
+
+ Gdk.Threads.AddIdle(1000, ()=>
+ {
+ _renderer.QueueDraw();
+
+ return true;
+ });
+ }
+
+ Thread.Sleep(timeToSleepMs);
+ }
+ }
+
+ public void Update(double frameTime)
+ {
+ _lastFrameTimeMs = frameTime;
+
+ // Get timing data if enough time has passed
+ _updateTimer += frameTime;
+
+ if (_doStep || ((Profile.UpdateRate > 0) && (!_pauseCheckbutton.Active && (_updateTimer > Profile.UpdateRate))))
+ {
+ _updateTimer = 0;
+ _captureTime = PerformanceCounter.ElapsedTicks;
+ _timingFlags = Profile.GetTimingFlags();
+ _doStep = false;
+ _profileUpdated = true;
+
+ _unsortedProfileData = Profile.GetProfilingData();
+
+ (_timingFlagsAverages, _timingFlagsLast) = Profile.GetTimingAveragesAndLast();
+ }
+
+ // Filtering
+ if (_profileUpdated)
+ {
+ lock (_profileDataLock)
+ {
+ _sortedProfileData = _showInactive.Active ? _unsortedProfileData : _unsortedProfileData.FindAll(kvp => kvp.Value.IsActive);
+
+ if (_sortAction != null)
+ {
+ _sortedProfileData.Sort(_sortAction);
+ }
+
+ if (_regexEnabled)
+ {
+ try
+ {
+ Regex filterRegex = new Regex(_filterText, RegexOptions.IgnoreCase);
+ if (_filterText != "")
+ {
+ _sortedProfileData = _sortedProfileData.Where((pair => filterRegex.IsMatch(pair.Key.Search))).ToList();
+ }
+ }
+ catch (ArgumentException argException)
+ {
+ // Skip filtering for invalid regex
+ }
+ }
+ else
+ {
+ // Regular filtering
+ _sortedProfileData = _sortedProfileData.Where((pair => pair.Key.Search.ToLower().Contains(_filterText.ToLower()))).ToList();
+ }
+ }
+
+ _profileUpdated = false;
+ _redrawPending = true;
+ _initComplete = true;
+ }
+ }
+
+ private string GetTimeString(long timestamp)
+ {
+ float time = (float)timestamp / PerformanceCounter.TicksPerMillisecond;
+
+ return (time < 1) ? $"{time * 1000:F3}us" : $"{time:F3}ms";
+ }
+
+ private void FilterBackspace()
+ {
+ if (_filterText.Length <= 1)
+ {
+ _filterText = "";
+ }
+ else
+ {
+ _filterText = _filterText.Remove(_filterText.Length - 1, 1);
+ }
+ }
+
+ private float GetLineY(float offset, float lineHeight, float padding, bool centre, int line)
+ {
+ return offset + lineHeight + padding + ((lineHeight + padding) * line) - ((centre) ? padding : 0);
+ }
+
+ public void Draw(SKCanvas canvas)
+ {
+ _lastOutputDraw = PerformanceCounter.ElapsedTicks;
+ if (!Visible ||
+ !_initComplete ||
+ !_enableCheckbutton.Active ||
+ !_redrawPending)
+ {
+ return;
+ }
+
+ float viewTop = TitleHeight + 5;
+ float viewBottom = _rendererHeight - FilterHeight - LineHeight;
+
+ float columnWidth;
+ float maxColumnWidth = MinimumColumnWidth;
+ float yOffset = _scrollPos + viewTop;
+ float xOffset = 10;
+ float timingWidth;
+
+ float contentHeight = GetLineY(0, LineHeight, LinePadding, false, _sortedProfileData.Count - 1);
+
+ _outputScrollbar.Adjustment.Upper = contentHeight;
+ _outputScrollbar.Adjustment.Lower = 0;
+ _outputScrollbar.Adjustment.PageSize = viewBottom - viewTop;
+
+
+ SKPaint textFont = new SKPaint()
+ {
+ Color = SKColors.White,
+ TextSize = LineHeight
+ };
+
+ SKPaint titleFont = new SKPaint()
+ {
+ Color = SKColors.White,
+ TextSize = TitleFontHeight
+ };
+
+ SKPaint evenItemBackground = new SKPaint()
+ {
+ Color = SKColors.Gray
+ };
+
+ canvas.Save();
+ canvas.ClipRect(new SKRect(0, viewTop, _rendererWidth, viewBottom), SKClipOperation.Intersect);
+
+ for (int i = 1; i < _sortedProfileData.Count; i += 2)
+ {
+ float top = GetLineY(yOffset, LineHeight, LinePadding, false, i - 1);
+ float bottom = GetLineY(yOffset, LineHeight, LinePadding, false, i);
+
+ canvas.DrawRect(new SKRect(0, top, _rendererWidth, bottom), evenItemBackground);
+ }
+
+ lock (_profileDataLock)
+ {
+ // Display category
+
+ for (int verticalIndex = 0; verticalIndex < _sortedProfileData.Count; verticalIndex++)
+ {
+ KeyValuePair entry = _sortedProfileData[verticalIndex];
+
+ if (entry.Key.Category == null)
+ {
+ continue;
+ }
+
+ float y = GetLineY(yOffset, LineHeight, LinePadding, true, verticalIndex);
+
+ canvas.DrawText(entry.Key.Category, new SKPoint(xOffset, y), textFont);
+
+ columnWidth = textFont.MeasureText(entry.Key.Category);
+
+ if (columnWidth > maxColumnWidth)
+ {
+ maxColumnWidth = columnWidth;
+ }
+ }
+
+ canvas.Restore();
+ canvas.DrawText("Category", new SKPoint(xOffset, TitleFontHeight + 2), titleFont);
+
+ columnWidth = titleFont.MeasureText("Category");
+
+ if (columnWidth > maxColumnWidth)
+ {
+ maxColumnWidth = columnWidth;
+ }
+
+ xOffset += maxColumnWidth + ColumnSpacing;
+
+ canvas.DrawLine(new SKPoint(xOffset - ColumnSpacing / 2, 0), new SKPoint(xOffset - ColumnSpacing / 2, viewBottom), textFont);
+
+ // Display session group
+ maxColumnWidth = MinimumColumnWidth;
+
+ canvas.Save();
+ canvas.ClipRect(new SKRect(0, viewTop, _rendererWidth, viewBottom), SKClipOperation.Intersect);
+
+ for (int verticalIndex = 0; verticalIndex < _sortedProfileData.Count; verticalIndex++)
+ {
+ KeyValuePair entry = _sortedProfileData[verticalIndex];
+
+ if (entry.Key.SessionGroup == null)
+ {
+ continue;
+ }
+
+ float y = GetLineY(yOffset, LineHeight, LinePadding, true, verticalIndex);
+
+ canvas.DrawText(entry.Key.SessionGroup, new SKPoint(xOffset, y), textFont);
+
+ columnWidth = textFont.MeasureText(entry.Key.SessionGroup);
+
+ if (columnWidth > maxColumnWidth)
+ {
+ maxColumnWidth = columnWidth;
+ }
+ }
+
+ canvas.Restore();
+ canvas.DrawText("Group", new SKPoint(xOffset, TitleFontHeight + 2), titleFont);
+
+ columnWidth = titleFont.MeasureText("Group");
+
+ if (columnWidth > maxColumnWidth)
+ {
+ maxColumnWidth = columnWidth;
+ }
+
+ xOffset += maxColumnWidth + ColumnSpacing;
+
+ canvas.DrawLine(new SKPoint(xOffset - ColumnSpacing / 2, 0), new SKPoint(xOffset - ColumnSpacing / 2, viewBottom), textFont);
+
+ // Display session item
+ maxColumnWidth = MinimumColumnWidth;
+
+ canvas.Save();
+ canvas.ClipRect(new SKRect(0, viewTop, _rendererWidth, viewBottom), SKClipOperation.Intersect);
+
+ for (int verticalIndex = 0; verticalIndex < _sortedProfileData.Count; verticalIndex++)
+ {
+ KeyValuePair entry = _sortedProfileData[verticalIndex];
+
+ if (entry.Key.SessionItem == null)
+ {
+ continue;
+ }
+
+ float y = GetLineY(yOffset, LineHeight, LinePadding, true, verticalIndex);
+
+ canvas.DrawText(entry.Key.SessionGroup, new SKPoint(xOffset, y), textFont);
+
+ columnWidth = textFont.MeasureText(entry.Key.SessionItem);
+
+ if (columnWidth > maxColumnWidth)
+ {
+ maxColumnWidth = columnWidth;
+ }
+ }
+
+ canvas.Restore();
+ canvas.DrawText("Item", new SKPoint(xOffset, TitleFontHeight + 2), titleFont);
+
+ columnWidth = titleFont.MeasureText("Item");
+
+ if (columnWidth > maxColumnWidth)
+ {
+ maxColumnWidth = columnWidth;
+ }
+
+ xOffset += maxColumnWidth + ColumnSpacing;
+
+ timingWidth = _rendererWidth - xOffset - 370;
+
+ canvas.Save();
+ canvas.ClipRect(new SKRect(0, viewTop, _rendererWidth, viewBottom), SKClipOperation.Intersect);
+ canvas.DrawLine(new SKPoint(xOffset, 0), new SKPoint(xOffset, _rendererHeight), textFont);
+
+ int mode = _modeBox.Active;
+
+ canvas.Save();
+ canvas.ClipRect(new SKRect(xOffset, yOffset,xOffset + timingWidth,yOffset + contentHeight),
+ SKClipOperation.Intersect);
+
+ switch (mode)
+ {
+ case 0:
+ DrawGraph(xOffset, yOffset, timingWidth, canvas);
+ break;
+ case 1:
+ DrawBars(xOffset, yOffset, timingWidth, canvas);
+
+ canvas.DrawText("Blue: Instant, Green: Avg, Red: Total",
+ new SKPoint(xOffset, _rendererHeight - TitleFontHeight), titleFont);
+ break;
+ }
+
+ canvas.Restore();
+ canvas.DrawLine(new SKPoint(xOffset + timingWidth, 0), new SKPoint(xOffset + timingWidth, _rendererHeight), textFont);
+
+ xOffset = _rendererWidth - 360;
+
+ // Display timestamps
+ long totalInstant = 0;
+ long totalAverage = 0;
+ long totalTime = 0;
+ long totalCount = 0;
+
+ for (int verticalIndex = 0; verticalIndex < _sortedProfileData.Count; verticalIndex++)
+ {
+ KeyValuePair entry = _sortedProfileData[verticalIndex];
+
+ float y = GetLineY(yOffset, LineHeight, LinePadding, true, verticalIndex);
+
+ canvas.DrawText($"{GetTimeString(entry.Value.Instant)} ({entry.Value.InstantCount})", new SKPoint(xOffset, y), textFont);
+ canvas.DrawText(GetTimeString(entry.Value.AverageTime), new SKPoint(150 + xOffset, y), textFont);
+ canvas.DrawText(GetTimeString(entry.Value.TotalTime), new SKPoint(260 + xOffset, y), textFont);
+
+ totalInstant += entry.Value.Instant;
+ totalAverage += entry.Value.AverageTime;
+ totalTime += entry.Value.TotalTime;
+ totalCount += entry.Value.InstantCount;
+ }
+
+ canvas.Restore();
+ canvas.DrawLine(new SKPoint(0, viewTop), new SKPoint(_rendererWidth, viewTop), titleFont);
+
+ float yHeight = 0 + TitleFontHeight;
+
+ canvas.DrawText("Instant (Count)", new SKPoint(xOffset, yHeight), titleFont);
+ canvas.DrawText("Average", new SKPoint(150 + xOffset, yHeight), titleFont);
+ canvas.DrawText("Total (ms)", new SKPoint(260 + xOffset, yHeight), titleFont);
+
+ // Totals
+ yHeight = _rendererHeight - FilterHeight + 3;
+
+ int textHeight = LineHeight - 2;
+
+ SKPaint detailFont = new SKPaint()
+ {
+ Color = new SKColor(100, 100, 255, 255),
+ TextSize = textHeight
+ };
+
+ canvas.DrawLine(new SkiaSharp.SKPoint(0, viewBottom), new SkiaSharp.SKPoint(_rendererWidth,viewBottom), textFont);
+
+ string hostTimeString = $"Host {GetTimeString(_timingFlagsLast[(int)TimingFlagType.SystemFrame])} " +
+ $"({GetTimeString(_timingFlagsAverages[(int)TimingFlagType.SystemFrame])})";
+
+ canvas.DrawText(hostTimeString, new SKPoint(5, yHeight), detailFont);
+
+ float tempWidth = detailFont.MeasureText(hostTimeString);
+
+ detailFont.Color = SKColors.Red;
+
+ string gameTimeString = $"Game {GetTimeString(_timingFlagsLast[(int)TimingFlagType.FrameSwap])} " +
+ $"({GetTimeString(_timingFlagsAverages[(int)TimingFlagType.FrameSwap])})";
+
+ canvas.DrawText(gameTimeString, new SKPoint(15 + tempWidth, yHeight), detailFont);
+
+ tempWidth += detailFont.MeasureText(gameTimeString);
+
+ detailFont.Color = SKColors.White;
+
+ canvas.DrawText($"Profiler: Update {GetTimeString(_lastOutputUpdateDuration)} Draw {GetTimeString(_lastOutputDrawDuration)}",
+ new SKPoint(20 + tempWidth, yHeight), detailFont);
+
+ detailFont.Color = SKColors.White;
+
+ canvas.DrawText($"{GetTimeString(totalInstant)} ({totalCount})", new SKPoint(xOffset, yHeight), detailFont);
+ canvas.DrawText(GetTimeString(totalAverage), new SKPoint(150 + xOffset, yHeight), detailFont);
+ canvas.DrawText(GetTimeString(totalTime), new SKPoint(260 + xOffset, yHeight), detailFont);
+
+ _lastOutputDrawDuration = PerformanceCounter.ElapsedTicks - _lastOutputDraw;
+ }
+ }
+
+ private void DrawGraph(float xOffset, float yOffset, float width, SKCanvas canvas)
+ {
+ if (_sortedProfileData.Count != 0)
+ {
+ int left, right;
+ float top, bottom;
+
+ float graphRight = xOffset + width;
+ float barHeight = (LineHeight - LinePadding);
+ long history = Profile.HistoryLength;
+ double timeWidthTicks = history / (double)_graphZoom;
+ long graphPositionTicks = (long)(_graphPosition * PerformanceCounter.TicksPerMillisecond);
+ long ticksPerPixel = (long)(timeWidthTicks / width);
+
+ // Reset start point if out of bounds
+ if (timeWidthTicks + graphPositionTicks > history)
+ {
+ graphPositionTicks = history - (long)timeWidthTicks;
+ _graphPosition = (float)graphPositionTicks / PerformanceCounter.TicksPerMillisecond;
+ }
+
+ graphPositionTicks = _captureTime - graphPositionTicks;
+
+ // Draw timing flags
+ if (_showFlags.Active)
+ {
+ TimingFlagType prevType = TimingFlagType.Count;
+
+ SKPaint timingPaint = new SKPaint
+ {
+ Color = _timingFlagColors.First()
+ };
+
+ foreach (TimingFlag timingFlag in _timingFlags)
+ {
+ if (prevType != timingFlag.FlagType)
+ {
+ prevType = timingFlag.FlagType;
+ timingPaint.Color = _timingFlagColors[(int)prevType];
+ }
+
+ int x = (int)(graphRight - ((graphPositionTicks - timingFlag.Timestamp) / timeWidthTicks) * width);
+
+ if (x > xOffset)
+ {
+ canvas.DrawLine(new SKPoint(x, yOffset), new SKPoint(x, _rendererHeight), timingPaint);
+ }
+ }
+ }
+
+ SKPaint barPaint = new SKPaint()
+ {
+ Color = SKColors.Green,
+ };
+
+ // Draw bars
+ for (int verticalIndex = 0; verticalIndex < _sortedProfileData.Count; verticalIndex++)
+ {
+ KeyValuePair entry = _sortedProfileData[verticalIndex];
+ long furthest = 0;
+
+ bottom = GetLineY(yOffset, LineHeight, LinePadding, false, verticalIndex);
+ top = bottom + barHeight;
+
+ // Skip rendering out of bounds bars
+ if (top < 0 || bottom > _rendererHeight)
+ {
+ continue;
+ }
+
+ barPaint.Color = SKColors.Green;
+
+ foreach (Timestamp timestamp in entry.Value.GetAllTimestamps())
+ {
+ // Skip drawing multiple timestamps on same pixel
+ if (timestamp.EndTime < furthest)
+ {
+ continue;
+ }
+
+ furthest = timestamp.EndTime + ticksPerPixel;
+
+ left = (int)(graphRight - ((graphPositionTicks - timestamp.BeginTime) / timeWidthTicks) * width);
+ right = (int)(graphRight - ((graphPositionTicks - timestamp.EndTime) / timeWidthTicks) * width);
+
+ left = (int)Math.Max(xOffset +1, left);
+
+ // Make sure width is at least 1px
+ right = Math.Max(left + 1, right);
+
+ canvas.DrawRect(new SKRect(left, top, right, bottom), barPaint);
+ }
+
+ // Currently capturing timestamp
+ barPaint.Color = SKColors.Red;
+
+ long entryBegin = entry.Value.BeginTime;
+
+ if (entryBegin != -1)
+ {
+ left = (int)(graphRight - ((graphPositionTicks - entryBegin) / timeWidthTicks) * width);
+
+ // Make sure width is at least 1px
+ left = Math.Min(left - 1, (int)graphRight);
+
+ left = (int)Math.Max(xOffset + 1, left);
+
+ canvas.DrawRect(new SKRect(left, top, graphRight, bottom), barPaint);
+ }
+ }
+
+ string label = $"-{MathF.Round(_graphPosition, 2)} ms";
+
+ SKPaint labelPaint = new SKPaint()
+ {
+ Color = SKColors.White,
+ TextSize = LineHeight
+ };
+
+ float labelWidth = labelPaint.MeasureText(label);
+
+ canvas.DrawText(label,new SKPoint(graphRight - labelWidth - LinePadding, FilterHeight + LinePadding) , labelPaint);
+
+ canvas.DrawText($"-{MathF.Round((float)((timeWidthTicks / PerformanceCounter.TicksPerMillisecond) + _graphPosition), 2)} ms",
+ new SKPoint(xOffset + LinePadding, FilterHeight + LinePadding), labelPaint);
+ }
+ }
+
+ private void DrawBars(float xOffset, float yOffset, float width, SKCanvas canvas)
+ {
+ if (_sortedProfileData.Count != 0)
+ {
+ long maxAverage = 0;
+ long maxTotal = 0;
+ long maxInstant = 0;
+
+ float barHeight = (LineHeight - LinePadding) / 3.0f;
+
+ // Get max values
+ foreach (KeyValuePair kvp in _sortedProfileData)
+ {
+ maxInstant = Math.Max(maxInstant, kvp.Value.Instant);
+ maxAverage = Math.Max(maxAverage, kvp.Value.AverageTime);
+ maxTotal = Math.Max(maxTotal, kvp.Value.TotalTime);
+ }
+
+ SKPaint barPaint = new SKPaint()
+ {
+ Color = SKColors.Blue
+ };
+
+ for (int verticalIndex = 0; verticalIndex < _sortedProfileData.Count; verticalIndex++)
+ {
+ KeyValuePair entry = _sortedProfileData[verticalIndex];
+ // Instant
+ barPaint.Color = SKColors.Blue;
+
+ float bottom = GetLineY(yOffset, LineHeight, LinePadding, false, verticalIndex);
+ float top = bottom + barHeight;
+ float right = (float)entry.Value.Instant / maxInstant * width + xOffset;
+
+ // Skip rendering out of bounds bars
+ if (top < 0 || bottom > _rendererHeight)
+ {
+ continue;
+ }
+
+ canvas.DrawRect(new SKRect(xOffset, top, right, bottom), barPaint);
+
+ // Average
+ barPaint.Color = SKColors.Green;
+
+ top += barHeight;
+ bottom += barHeight;
+ right = (float)entry.Value.AverageTime / maxAverage * width + xOffset;
+
+ canvas.DrawRect(new SKRect(xOffset, top, right, bottom), barPaint);
+
+ // Total
+ barPaint.Color = SKColors.Red;
+
+ top += barHeight;
+ bottom += barHeight;
+ right = (float)entry.Value.TotalTime / maxTotal * width + xOffset;
+
+ canvas.DrawRect(new SKRect(xOffset, top, right, bottom), barPaint);
+ }
+ }
+ }
+ }
+}
diff --git a/Ryujinx.Debugger/UI/ProfilerWidget.glade b/Ryujinx.Debugger/UI/ProfilerWidget.glade
new file mode 100644
index 0000000000..00dd4f7044
--- /dev/null
+++ b/Ryujinx.Debugger/UI/ProfilerWidget.glade
@@ -0,0 +1,232 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ Graph
+
+
+ 1
+ Bars
+
+
+
+
+ ProfilerBox
+ True
+ False
+ 5
+ 5
+ 5
+ 5
+ vertical
+ 10
+
+
+ Enable Profiler
+ True
+ True
+ False
+ True
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+
+
+ True
+ True
+ never
+ in
+
+
+
+
+
+ True
+ True
+ 0
+
+
+
+
+ True
+ False
+ vertical
+
+
+ False
+ True
+ 1
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ True
+ False
+ 10
+
+
+ Show Inactive
+ True
+ True
+ False
+ True
+ True
+
+
+ False
+ True
+ 0
+
+
+
+
+ Show Flags
+ True
+ True
+ False
+ True
+ True
+
+
+ False
+ True
+ 1
+
+
+
+
+ Paused
+ True
+ True
+ False
+ True
+
+
+ False
+ True
+ 2
+
+
+
+
+ True
+ False
+
+
+ True
+ False
+ View Mode:
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ viewMode
+ 0
+
+
+
+ 1
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ False
+ True
+ 3
+
+
+
+
+ True
+ False
+
+
+ True
+ False
+ Filter:
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ True
+
+
+ False
+ True
+ 1
+
+
+
+
+ False
+ True
+ 4
+
+
+
+
+ Step
+ True
+ True
+ True
+
+
+ False
+ True
+ 5
+
+
+
+
+ False
+ True
+ 2
+
+
+
+
diff --git a/Ryujinx.Debugger/UI/SkRenderer.cs b/Ryujinx.Debugger/UI/SkRenderer.cs
new file mode 100644
index 0000000000..a95e4542cc
--- /dev/null
+++ b/Ryujinx.Debugger/UI/SkRenderer.cs
@@ -0,0 +1,23 @@
+using SkiaSharp;
+using SkiaSharp.Views.Gtk;
+using System;
+
+namespace Ryujinx.Debugger.UI
+{
+ public class SkRenderer : SKDrawingArea
+ {
+ public event EventHandler DrawGraphs;
+
+ public SkRenderer()
+ {
+ this.PaintSurface += SkRenderer_PaintSurface;
+ }
+
+ private void SkRenderer_PaintSurface(object sender, SkiaSharp.Views.Desktop.SKPaintSurfaceEventArgs e)
+ {
+ e.Surface.Canvas.Clear(SKColors.Black);
+
+ DrawGraphs.Invoke(this, e);
+ }
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/IpcService.cs b/Ryujinx.HLE/HOS/Services/IpcService.cs
index e953868358..67df453e5c 100644
--- a/Ryujinx.HLE/HOS/Services/IpcService.cs
+++ b/Ryujinx.HLE/HOS/Services/IpcService.cs
@@ -6,7 +6,7 @@ using Ryujinx.HLE.HOS.Kernel.Ipc;
using System;
using System.Collections.Generic;
using System.IO;
-using Ryujinx.Profiler;
+using Ryujinx.Debugger.Profiler;
using System.Reflection;
using System.Linq;
diff --git a/Ryujinx.HLE/PerformanceStatistics.cs b/Ryujinx.HLE/PerformanceStatistics.cs
index 896ab67b09..5abf2628ee 100644
--- a/Ryujinx.HLE/PerformanceStatistics.cs
+++ b/Ryujinx.HLE/PerformanceStatistics.cs
@@ -1,4 +1,4 @@
-using Ryujinx.Profiler;
+using Ryujinx.Debugger.Profiler;
using System.Diagnostics;
using System.Timers;
diff --git a/Ryujinx.HLE/Ryujinx.HLE.csproj b/Ryujinx.HLE/Ryujinx.HLE.csproj
index ac4b822401..e9540ab876 100644
--- a/Ryujinx.HLE/Ryujinx.HLE.csproj
+++ b/Ryujinx.HLE/Ryujinx.HLE.csproj
@@ -12,7 +12,7 @@
true
- TRACE;USE_PROFILING
+ TRACE;USE_DEBUGGING
false
@@ -22,7 +22,7 @@
true
- TRACE;USE_PROFILING
+ TRACE;USE_DEBUGGING
true
@@ -44,7 +44,7 @@
-
+
diff --git a/Ryujinx.LLE/Luea.csproj b/Ryujinx.LLE/Luea.csproj
index 7eb546d8fe..0184d1be75 100644
--- a/Ryujinx.LLE/Luea.csproj
+++ b/Ryujinx.LLE/Luea.csproj
@@ -8,12 +8,12 @@
- TRACE;USE_PROFILING
+ TRACE;USE_DEBUGGING
true
- TRACE;USE_PROFILING
+ TRACE;USE_DEBUGGING
false
diff --git a/Ryujinx.Profiler/ProfilerKeyboardHandler.cs b/Ryujinx.Profiler/ProfilerKeyboardHandler.cs
deleted file mode 100644
index e6207e8924..0000000000
--- a/Ryujinx.Profiler/ProfilerKeyboardHandler.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using OpenTK.Input;
-
-namespace Ryujinx.Profiler
-{
- public struct ProfilerButtons
- {
- public Key ToggleProfiler;
- }
-
- public class ProfilerKeyboardHandler
- {
- public ProfilerButtons Buttons;
-
- private KeyboardState _prevKeyboard;
-
- public ProfilerKeyboardHandler(ProfilerButtons buttons)
- {
- Buttons = buttons;
- }
-
- public bool TogglePressed(KeyboardState keyboard) => !keyboard[Buttons.ToggleProfiler] && _prevKeyboard[Buttons.ToggleProfiler];
-
- public void SetPrevKeyboardState(KeyboardState keyboard)
- {
- _prevKeyboard = keyboard;
- }
- }
-}
diff --git a/Ryujinx.Profiler/Ryujinx.Profiler.csproj b/Ryujinx.Profiler/Ryujinx.Profiler.csproj
deleted file mode 100644
index 0e089ccf52..0000000000
--- a/Ryujinx.Profiler/Ryujinx.Profiler.csproj
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-
- netcoreapp3.0
- win-x64;osx-x64;linux-x64
- true
- Debug;Release;Profile Debug;Profile Release
-
-
-
- TRACE
-
-
-
- TRACE;USE_PROFILING
- false
-
-
-
- TRACE;USE_PROFILING
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
- PreserveNewest
-
-
-
-
diff --git a/Ryujinx.Profiler/UI/ProfileButton.cs b/Ryujinx.Profiler/UI/ProfileButton.cs
deleted file mode 100644
index 7e2ae72884..0000000000
--- a/Ryujinx.Profiler/UI/ProfileButton.cs
+++ /dev/null
@@ -1,110 +0,0 @@
-using System;
-using OpenTK;
-using OpenTK.Graphics.OpenGL;
-using Ryujinx.Profiler.UI.SharpFontHelpers;
-
-namespace Ryujinx.Profiler.UI
-{
- public class ProfileButton
- {
- // Store font service
- private FontService _fontService;
-
- // Layout information
- private int _left, _right;
- private int _bottom, _top;
- private int _height;
- private int _padding;
-
- // Label information
- private int _labelX, _labelY;
- private string _label;
-
- // Misc
- private Action _clicked;
- private bool _visible;
-
- public ProfileButton(FontService fontService, Action clicked)
- : this(fontService, clicked, 0, 0, 0, 0, 0)
- {
- _visible = false;
- }
-
- public ProfileButton(FontService fontService, Action clicked, int x, int y, int padding, int height, int width)
- : this(fontService, "", clicked, x, y, padding, height, width)
- {
- _visible = false;
- }
-
- public ProfileButton(FontService fontService, string label, Action clicked, int x, int y, int padding, int height, int width = -1)
- {
- _fontService = fontService;
- _clicked = clicked;
-
- UpdateSize(label, x, y, padding, height, width);
- }
-
- public int UpdateSize(string label, int x, int y, int padding, int height, int width = -1)
- {
- _visible = true;
- _label = label;
-
- if (width == -1)
- {
- // Dummy draw to measure size
- width = (int)_fontService.DrawText(label, 0, 0, height, false);
- }
-
- UpdateSize(x, y, padding, width, height);
-
- return _right - _left;
- }
-
- public void UpdateSize(int x, int y, int padding, int width, int height)
- {
- _height = height;
- _left = x;
- _bottom = y;
- _labelX = x + padding / 2;
- _labelY = y + padding / 2;
- _top = y + height + padding;
- _right = x + width + padding;
- }
-
- public void Draw()
- {
- if (!_visible)
- {
- return;
- }
-
- // Draw backing rectangle
- GL.Begin(PrimitiveType.Triangles);
- GL.Color3(Color.Black);
- GL.Vertex2(_left, _bottom);
- GL.Vertex2(_left, _top);
- GL.Vertex2(_right, _top);
-
- GL.Vertex2(_right, _top);
- GL.Vertex2(_right, _bottom);
- GL.Vertex2(_left, _bottom);
- GL.End();
-
- // Use font service to draw label
- _fontService.DrawText(_label, _labelX, _labelY, _height);
- }
-
- public bool ProcessClick(int x, int y)
- {
- // If button contains x, y
- if (x > _left && x < _right &&
- y > _bottom && y < _top)
- {
- _clicked();
- return true;
- }
-
- return false;
- }
- }
-}
diff --git a/Ryujinx.Profiler/UI/ProfileWindow.cs b/Ryujinx.Profiler/UI/ProfileWindow.cs
deleted file mode 100644
index 1db70bc791..0000000000
--- a/Ryujinx.Profiler/UI/ProfileWindow.cs
+++ /dev/null
@@ -1,773 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Linq;
-using System.Text.RegularExpressions;
-using OpenTK;
-using OpenTK.Graphics;
-using OpenTK.Graphics.OpenGL;
-using OpenTK.Input;
-using Ryujinx.Common;
-using Ryujinx.Profiler.UI.SharpFontHelpers;
-
-namespace Ryujinx.Profiler.UI
-{
- public partial class ProfileWindow : GameWindow
- {
- // List all buttons for index in button array
- private enum ButtonIndex
- {
- TagTitle = 0,
- InstantTitle = 1,
- AverageTitle = 2,
- TotalTitle = 3,
- FilterBar = 4,
- ShowHideInactive = 5,
- Pause = 6,
- ChangeDisplay = 7,
-
- // Don't automatically draw after here
- ToggleFlags = 8,
- Step = 9,
-
- // Update this when new buttons are added.
- // These are indexes to the enum list
- Autodraw = 8,
- Count = 10,
- }
-
- // Font service
- private FontService _fontService;
-
- // UI variables
- private ProfileButton[] _buttons;
-
- private bool _initComplete = false;
- private bool _visible = true;
- private bool _visibleChanged = true;
- private bool _viewportUpdated = true;
- private bool _redrawPending = true;
- private bool _displayGraph = true;
- private bool _displayFlags = true;
- private bool _showInactive = true;
- private bool _paused = false;
- private bool _doStep = false;
-
- // Layout
- private const int LineHeight = 16;
- private const int TitleHeight = 24;
- private const int TitleFontHeight = 16;
- private const int LinePadding = 2;
- private const int ColumnSpacing = 15;
- private const int FilterHeight = 24;
- private const int BottomBarHeight = FilterHeight + LineHeight;
-
- // Sorting
- private List> _unsortedProfileData;
- private IComparer> _sortAction = new ProfileSorters.TagAscending();
-
- // Flag data
- private long[] _timingFlagsAverages;
- private long[] _timingFlagsLast;
-
- // Filtering
- private string _filterText = "";
- private bool _regexEnabled = false;
-
- // Scrolling
- private float _scrollPos = 0;
- private float _minScroll = 0;
- private float _maxScroll = 0;
-
- // Profile data storage
- private List> _sortedProfileData;
- private long _captureTime;
-
- // Input
- private bool _backspaceDown = false;
- private bool _prevBackspaceDown = false;
- private double _backspaceDownTime = 0;
-
- // F35 used as no key
- private Key _graphControlKey = Key.F35;
-
- // Event management
- private double _updateTimer;
- private double _processEventTimer;
- private bool _profileUpdated = false;
- private readonly object _profileDataLock = new object();
-
- public ProfileWindow()
- // Graphics mode enables 2xAA
- : base(1280, 720, new GraphicsMode(new ColorFormat(8, 8, 8, 8), 1, 1, 2))
- {
- Title = "Profiler";
- Location = new Point(DisplayDevice.Default.Width - 1280,
- (DisplayDevice.Default.Height - 720) - 50);
-
- if (Profile.UpdateRate <= 0)
- {
- // Perform step regardless of flag type
- Profile.RegisterFlagReceiver((t) =>
- {
- if (!_paused)
- {
- _doStep = true;
- }
- });
- }
-
- // Large number to force an update on first update
- _updateTimer = 0xFFFF;
-
- Init();
-
- // Release context for render thread
- Context.MakeCurrent(null);
- }
-
- public void ToggleVisible()
- {
- _visible = !_visible;
- _visibleChanged = true;
- }
-
- private void SetSort(IComparer> filter)
- {
- _sortAction = filter;
- _profileUpdated = true;
- }
-
-#region OnLoad
- ///
- /// Setup OpenGL and load resources
- ///
- public void Init()
- {
- GL.ClearColor(Color.Black);
- _fontService = new FontService();
- _fontService.InitializeTextures();
- _fontService.UpdateScreenHeight(Height);
-
- _buttons = new ProfileButton[(int)ButtonIndex.Count];
- _buttons[(int)ButtonIndex.TagTitle] = new ProfileButton(_fontService, () => SetSort(new ProfileSorters.TagAscending()));
- _buttons[(int)ButtonIndex.InstantTitle] = new ProfileButton(_fontService, () => SetSort(new ProfileSorters.InstantAscending()));
- _buttons[(int)ButtonIndex.AverageTitle] = new ProfileButton(_fontService, () => SetSort(new ProfileSorters.AverageAscending()));
- _buttons[(int)ButtonIndex.TotalTitle] = new ProfileButton(_fontService, () => SetSort(new ProfileSorters.TotalAscending()));
- _buttons[(int)ButtonIndex.Step] = new ProfileButton(_fontService, () => _doStep = true);
- _buttons[(int)ButtonIndex.FilterBar] = new ProfileButton(_fontService, () =>
- {
- _profileUpdated = true;
- _regexEnabled = !_regexEnabled;
- });
-
- _buttons[(int)ButtonIndex.ShowHideInactive] = new ProfileButton(_fontService, () =>
- {
- _profileUpdated = true;
- _showInactive = !_showInactive;
- });
-
- _buttons[(int)ButtonIndex.Pause] = new ProfileButton(_fontService, () =>
- {
- _profileUpdated = true;
- _paused = !_paused;
- });
-
- _buttons[(int)ButtonIndex.ToggleFlags] = new ProfileButton(_fontService, () =>
- {
- _displayFlags = !_displayFlags;
- _redrawPending = true;
- });
-
- _buttons[(int)ButtonIndex.ChangeDisplay] = new ProfileButton(_fontService, () =>
- {
- _displayGraph = !_displayGraph;
- _redrawPending = true;
- });
-
- Visible = _visible;
- }
-#endregion
-
-#region OnResize
- ///
- /// Respond to resize events
- ///
- /// Contains information on the new GameWindow size.
- /// There is no need to call the base implementation.
- protected override void OnResize(EventArgs e)
- {
- _viewportUpdated = true;
- }
-#endregion
-
-#region OnClose
- ///
- /// Intercept close event and hide instead
- ///
- protected override void OnClosing(CancelEventArgs e)
- {
- // Hide window
- _visible = false;
- _visibleChanged = true;
-
- // Cancel close
- e.Cancel = true;
-
- base.OnClosing(e);
- }
-#endregion
-
-#region OnUpdateFrame
- ///
- /// Profile Update Loop
- ///
- /// Contains timing information.
- /// There is no need to call the base implementation.
- public void Update(FrameEventArgs e)
- {
- if (_visibleChanged)
- {
- Visible = _visible;
- _visibleChanged = false;
- }
-
- // Backspace handling
- if (_backspaceDown)
- {
- if (!_prevBackspaceDown)
- {
- _backspaceDownTime = 0;
- FilterBackspace();
- }
- else
- {
- _backspaceDownTime += e.Time;
- if (_backspaceDownTime > 0.3)
- {
- _backspaceDownTime -= 0.05;
- FilterBackspace();
- }
- }
- }
- _prevBackspaceDown = _backspaceDown;
-
- // Get timing data if enough time has passed
- _updateTimer += e.Time;
- if (_doStep || ((Profile.UpdateRate > 0) && (!_paused && (_updateTimer > Profile.UpdateRate))))
- {
- _updateTimer = 0;
- _captureTime = PerformanceCounter.ElapsedTicks;
- _timingFlags = Profile.GetTimingFlags();
- _doStep = false;
- _profileUpdated = true;
-
- _unsortedProfileData = Profile.GetProfilingData();
- (_timingFlagsAverages, _timingFlagsLast) = Profile.GetTimingAveragesAndLast();
-
- }
-
- // Filtering
- if (_profileUpdated)
- {
- lock (_profileDataLock)
- {
- _sortedProfileData = _showInactive ? _unsortedProfileData : _unsortedProfileData.FindAll(kvp => kvp.Value.IsActive);
-
- if (_sortAction != null)
- {
- _sortedProfileData.Sort(_sortAction);
- }
-
- if (_regexEnabled)
- {
- try
- {
- Regex filterRegex = new Regex(_filterText, RegexOptions.IgnoreCase);
- if (_filterText != "")
- {
- _sortedProfileData = _sortedProfileData.Where((pair => filterRegex.IsMatch(pair.Key.Search))).ToList();
- }
- }
- catch (ArgumentException argException)
- {
- // Skip filtering for invalid regex
- }
- }
- else
- {
- // Regular filtering
- _sortedProfileData = _sortedProfileData.Where((pair => pair.Key.Search.ToLower().Contains(_filterText.ToLower()))).ToList();
- }
- }
-
- _profileUpdated = false;
- _redrawPending = true;
- _initComplete = true;
- }
-
- // Check for events 20 times a second
- _processEventTimer += e.Time;
- if (_processEventTimer > 0.05)
- {
- ProcessEvents();
-
- if (_graphControlKey != Key.F35)
- {
- switch (_graphControlKey)
- {
- case Key.Left:
- _graphPosition += (long) (GraphMoveSpeed * e.Time);
- break;
-
- case Key.Right:
- _graphPosition = Math.Max(_graphPosition - (long) (GraphMoveSpeed * e.Time), 0);
- break;
-
- case Key.Up:
- _graphZoom = MathF.Min(_graphZoom + (float) (GraphZoomSpeed * e.Time), 100.0f);
- break;
-
- case Key.Down:
- _graphZoom = MathF.Max(_graphZoom - (float) (GraphZoomSpeed * e.Time), 1f);
- break;
- }
-
- _redrawPending = true;
- }
-
- _processEventTimer = 0;
- }
- }
-#endregion
-
-#region OnRenderFrame
- ///
- /// Profile Render Loop
- ///
- /// There is no need to call the base implementation.
- public void Draw()
- {
- if (!_visible || !_initComplete)
- {
- return;
- }
-
- // Update viewport
- if (_viewportUpdated)
- {
- GL.Viewport(0, 0, Width, Height);
-
- GL.MatrixMode(MatrixMode.Projection);
- GL.LoadIdentity();
- GL.Ortho(0, Width, 0, Height, 0.0, 4.0);
-
- _fontService.UpdateScreenHeight(Height);
-
- _viewportUpdated = false;
- _redrawPending = true;
- }
-
- if (!_redrawPending)
- {
- return;
- }
-
- // Frame setup
- GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
- GL.ClearColor(Color.Black);
-
- _fontService.fontColor = Color.White;
- int verticalIndex = 0;
-
- float width;
- float maxWidth = 0;
- float yOffset = _scrollPos - TitleHeight;
- float xOffset = 10;
- float timingDataLeft;
- float timingWidth;
-
- // Background lines to make reading easier
- #region Background Lines
- GL.Enable(EnableCap.ScissorTest);
- GL.Scissor(0, BottomBarHeight, Width, Height - TitleHeight - BottomBarHeight);
- GL.Begin(PrimitiveType.Triangles);
- GL.Color3(0.2f, 0.2f, 0.2f);
- for (int i = 0; i < _sortedProfileData.Count; i += 2)
- {
- float top = GetLineY(yOffset, LineHeight, LinePadding, false, i - 1);
- float bottom = GetLineY(yOffset, LineHeight, LinePadding, false, i);
-
- // Skip rendering out of bounds bars
- if (top < 0 || bottom > Height)
- continue;
-
- GL.Vertex2(0, bottom);
- GL.Vertex2(0, top);
- GL.Vertex2(Width, top);
-
- GL.Vertex2(Width, top);
- GL.Vertex2(Width, bottom);
- GL.Vertex2(0, bottom);
- }
- GL.End();
- _maxScroll = (LineHeight + LinePadding) * (_sortedProfileData.Count - 1);
-#endregion
-
- lock (_profileDataLock)
- {
-// Display category
-#region Category
- verticalIndex = 0;
- foreach (var entry in _sortedProfileData)
- {
- if (entry.Key.Category == null)
- {
- verticalIndex++;
- continue;
- }
-
- float y = GetLineY(yOffset, LineHeight, LinePadding, true, verticalIndex++);
- width = _fontService.DrawText(entry.Key.Category, xOffset, y, LineHeight);
-
- if (width > maxWidth)
- {
- maxWidth = width;
- }
- }
- GL.Disable(EnableCap.ScissorTest);
-
- width = _fontService.DrawText("Category", xOffset, Height - TitleFontHeight, TitleFontHeight);
- if (width > maxWidth)
- maxWidth = width;
-
- xOffset += maxWidth + ColumnSpacing;
-#endregion
-
-// Display session group
-#region Session Group
- maxWidth = 0;
- verticalIndex = 0;
-
- GL.Enable(EnableCap.ScissorTest);
- foreach (var entry in _sortedProfileData)
- {
- if (entry.Key.SessionGroup == null)
- {
- verticalIndex++;
- continue;
- }
-
- float y = GetLineY(yOffset, LineHeight, LinePadding, true, verticalIndex++);
- width = _fontService.DrawText(entry.Key.SessionGroup, xOffset, y, LineHeight);
-
- if (width > maxWidth)
- {
- maxWidth = width;
- }
- }
- GL.Disable(EnableCap.ScissorTest);
-
- width = _fontService.DrawText("Group", xOffset, Height - TitleFontHeight, TitleFontHeight);
- if (width > maxWidth)
- maxWidth = width;
-
- xOffset += maxWidth + ColumnSpacing;
-#endregion
-
-// Display session item
-#region Session Item
- maxWidth = 0;
- verticalIndex = 0;
- GL.Enable(EnableCap.ScissorTest);
- foreach (var entry in _sortedProfileData)
- {
- if (entry.Key.SessionItem == null)
- {
- verticalIndex++;
- continue;
- }
-
- float y = GetLineY(yOffset, LineHeight, LinePadding, true, verticalIndex++);
- width = _fontService.DrawText(entry.Key.SessionItem, xOffset, y, LineHeight);
-
- if (width > maxWidth)
- {
- maxWidth = width;
- }
- }
- GL.Disable(EnableCap.ScissorTest);
-
- width = _fontService.DrawText("Item", xOffset, Height - TitleFontHeight, TitleFontHeight);
- if (width > maxWidth)
- maxWidth = width;
-
- xOffset += maxWidth + ColumnSpacing;
- _buttons[(int)ButtonIndex.TagTitle].UpdateSize(0, Height - TitleFontHeight, 0, (int)xOffset, TitleFontHeight);
-#endregion
-
- // Timing data
- timingWidth = Width - xOffset - 370;
- timingDataLeft = xOffset;
-
- GL.Scissor((int)xOffset, BottomBarHeight, (int)timingWidth, Height - TitleHeight - BottomBarHeight);
-
- if (_displayGraph)
- {
- DrawGraph(xOffset, yOffset, timingWidth);
- }
- else
- {
- DrawBars(xOffset, yOffset, timingWidth);
- }
-
- GL.Scissor(0, BottomBarHeight, Width, Height - TitleHeight - BottomBarHeight);
-
- if (!_displayGraph)
- {
- _fontService.DrawText("Blue: Instant, Green: Avg, Red: Total", xOffset, Height - TitleFontHeight, TitleFontHeight);
- }
-
- xOffset = Width - 360;
-
-// Display timestamps
-#region Timestamps
- verticalIndex = 0;
- long totalInstant = 0;
- long totalAverage = 0;
- long totalTime = 0;
- long totalCount = 0;
-
- GL.Enable(EnableCap.ScissorTest);
- foreach (var entry in _sortedProfileData)
- {
- float y = GetLineY(yOffset, LineHeight, LinePadding, true, verticalIndex++);
-
- _fontService.DrawText($"{GetTimeString(entry.Value.Instant)} ({entry.Value.InstantCount})", xOffset, y, LineHeight);
-
- _fontService.DrawText(GetTimeString(entry.Value.AverageTime), 150 + xOffset, y, LineHeight);
-
- _fontService.DrawText(GetTimeString(entry.Value.TotalTime), 260 + xOffset, y, LineHeight);
-
- totalInstant += entry.Value.Instant;
- totalAverage += entry.Value.AverageTime;
- totalTime += entry.Value.TotalTime;
- totalCount += entry.Value.InstantCount;
- }
- GL.Disable(EnableCap.ScissorTest);
-
- float yHeight = Height - TitleFontHeight;
-
- _fontService.DrawText("Instant (Count)", xOffset, yHeight, TitleFontHeight);
- _buttons[(int)ButtonIndex.InstantTitle].UpdateSize((int)xOffset, (int)yHeight, 0, 130, TitleFontHeight);
-
- _fontService.DrawText("Average", 150 + xOffset, yHeight, TitleFontHeight);
- _buttons[(int)ButtonIndex.AverageTitle].UpdateSize((int)(150 + xOffset), (int)yHeight, 0, 130, TitleFontHeight);
-
- _fontService.DrawText("Total (ms)", 260 + xOffset, yHeight, TitleFontHeight);
- _buttons[(int)ButtonIndex.TotalTitle].UpdateSize((int)(260 + xOffset), (int)yHeight, 0, Width, TitleFontHeight);
-
- // Totals
- yHeight = FilterHeight + 3;
- int textHeight = LineHeight - 2;
-
- _fontService.fontColor = new Color(100, 100, 255, 255);
- float tempWidth = _fontService.DrawText($"Host {GetTimeString(_timingFlagsLast[(int)TimingFlagType.SystemFrame])} " +
- $"({GetTimeString(_timingFlagsAverages[(int)TimingFlagType.SystemFrame])})", 5, yHeight, textHeight);
-
- _fontService.fontColor = Color.Red;
- _fontService.DrawText($"Game {GetTimeString(_timingFlagsLast[(int)TimingFlagType.FrameSwap])} " +
- $"({GetTimeString(_timingFlagsAverages[(int)TimingFlagType.FrameSwap])})", 15 + tempWidth, yHeight, textHeight);
- _fontService.fontColor = Color.White;
-
-
- _fontService.DrawText($"{GetTimeString(totalInstant)} ({totalCount})", xOffset, yHeight, textHeight);
- _fontService.DrawText(GetTimeString(totalAverage), 150 + xOffset, yHeight, textHeight);
- _fontService.DrawText(GetTimeString(totalTime), 260 + xOffset, yHeight, textHeight);
-#endregion
- }
-
-#region Bottom bar
- // Show/Hide Inactive
- float widthShowHideButton = _buttons[(int)ButtonIndex.ShowHideInactive].UpdateSize($"{(_showInactive ? "Hide" : "Show")} Inactive", 5, 5, 4, 16);
-
- // Play/Pause
- float widthPlayPauseButton = _buttons[(int)ButtonIndex.Pause].UpdateSize(_paused ? "Play" : "Pause", 15 + (int)widthShowHideButton, 5, 4, 16) + widthShowHideButton;
-
- // Step
- float widthStepButton = widthPlayPauseButton;
-
- if (_paused)
- {
- widthStepButton += _buttons[(int)ButtonIndex.Step].UpdateSize("Step", (int)(25 + widthPlayPauseButton), 5, 4, 16) + 10;
- _buttons[(int)ButtonIndex.Step].Draw();
- }
-
- // Change display
- float widthChangeDisplay = _buttons[(int)ButtonIndex.ChangeDisplay].UpdateSize($"View: {(_displayGraph ? "Graph" : "Bars")}", 25 + (int)widthStepButton, 5, 4, 16) + widthStepButton;
-
- width = widthChangeDisplay;
-
- if (_displayGraph)
- {
- width += _buttons[(int) ButtonIndex.ToggleFlags].UpdateSize($"{(_displayFlags ? "Hide" : "Show")} Flags", 35 + (int)widthChangeDisplay, 5, 4, 16) + 10;
- _buttons[(int)ButtonIndex.ToggleFlags].Draw();
- }
-
- // Filter bar
- _fontService.DrawText($"{(_regexEnabled ? "Regex " : "Filter")}: {_filterText}", 35 + width, 7, 16);
- _buttons[(int)ButtonIndex.FilterBar].UpdateSize((int)(45 + width), 0, 0, Width, FilterHeight);
-#endregion
-
- // Draw buttons
- for (int i = 0; i < (int)ButtonIndex.Autodraw; i++)
- {
- _buttons[i].Draw();
- }
-
-// Dividing lines
-#region Dividing lines
- GL.Color3(Color.White);
- GL.Begin(PrimitiveType.Lines);
- // Top divider
- GL.Vertex2(0, Height -TitleHeight);
- GL.Vertex2(Width, Height - TitleHeight);
-
- // Bottom divider
- GL.Vertex2(0, FilterHeight);
- GL.Vertex2(Width, FilterHeight);
-
- GL.Vertex2(0, BottomBarHeight);
- GL.Vertex2(Width, BottomBarHeight);
-
- // Bottom vertical dividers
- GL.Vertex2(widthShowHideButton + 10, 0);
- GL.Vertex2(widthShowHideButton + 10, FilterHeight);
-
- GL.Vertex2(widthPlayPauseButton + 20, 0);
- GL.Vertex2(widthPlayPauseButton + 20, FilterHeight);
-
- if (_paused)
- {
- GL.Vertex2(widthStepButton + 20, 0);
- GL.Vertex2(widthStepButton + 20, FilterHeight);
- }
-
- if (_displayGraph)
- {
- GL.Vertex2(widthChangeDisplay + 30, 0);
- GL.Vertex2(widthChangeDisplay + 30, FilterHeight);
- }
-
- GL.Vertex2(width + 30, 0);
- GL.Vertex2(width + 30, FilterHeight);
-
- // Column dividers
- float timingDataTop = Height - TitleHeight;
-
- GL.Vertex2(timingDataLeft, FilterHeight);
- GL.Vertex2(timingDataLeft, timingDataTop);
-
- GL.Vertex2(timingWidth + timingDataLeft, FilterHeight);
- GL.Vertex2(timingWidth + timingDataLeft, timingDataTop);
- GL.End();
-#endregion
-
- _redrawPending = false;
- SwapBuffers();
- }
-#endregion
-
- private string GetTimeString(long timestamp)
- {
- float time = (float)timestamp / PerformanceCounter.TicksPerMillisecond;
- return (time < 1) ? $"{time * 1000:F3}us" : $"{time:F3}ms";
- }
-
- private void FilterBackspace()
- {
- if (_filterText.Length <= 1)
- {
- _filterText = "";
- }
- else
- {
- _filterText = _filterText.Remove(_filterText.Length - 1, 1);
- }
- }
-
- private float GetLineY(float offset, float lineHeight, float padding, bool centre, int line)
- {
- return Height + offset - lineHeight - padding - ((lineHeight + padding) * line) + ((centre) ? padding : 0);
- }
-
- protected override void OnKeyPress(KeyPressEventArgs e)
- {
- _filterText += e.KeyChar;
- _profileUpdated = true;
- }
-
- protected override void OnKeyDown(KeyboardKeyEventArgs e)
- {
- switch (e.Key)
- {
- case Key.BackSpace:
- _profileUpdated = _backspaceDown = true;
- return;
-
- case Key.Left:
- case Key.Right:
- case Key.Up:
- case Key.Down:
- _graphControlKey = e.Key;
- return;
- }
- base.OnKeyUp(e);
- }
-
- protected override void OnKeyUp(KeyboardKeyEventArgs e)
- {
- // Can't go into switch as value isn't constant
- if (e.Key == Profile.Controls.Buttons.ToggleProfiler)
- {
- ToggleVisible();
- return;
- }
-
- switch (e.Key)
- {
- case Key.BackSpace:
- _backspaceDown = false;
- return;
-
- case Key.Left:
- case Key.Right:
- case Key.Up:
- case Key.Down:
- _graphControlKey = Key.F35;
- return;
- }
- base.OnKeyUp(e);
- }
-
- protected override void OnMouseUp(MouseButtonEventArgs e)
- {
- foreach (ProfileButton button in _buttons)
- {
- if (button.ProcessClick(e.X, Height - e.Y))
- return;
- }
- }
-
- protected override void OnMouseWheel(MouseWheelEventArgs e)
- {
- _scrollPos += e.Delta * -30;
- if (_scrollPos < _minScroll)
- _scrollPos = _minScroll;
- if (_scrollPos > _maxScroll)
- _scrollPos = _maxScroll;
-
- _redrawPending = true;
- }
- }
-}
\ No newline at end of file
diff --git a/Ryujinx.Profiler/UI/ProfileWindowBars.cs b/Ryujinx.Profiler/UI/ProfileWindowBars.cs
deleted file mode 100644
index ab5b4fd131..0000000000
--- a/Ryujinx.Profiler/UI/ProfileWindowBars.cs
+++ /dev/null
@@ -1,85 +0,0 @@
-using System;
-using System.Collections.Generic;
-using OpenTK;
-using OpenTK.Graphics.OpenGL;
-
-namespace Ryujinx.Profiler.UI
-{
- public partial class ProfileWindow
- {
- private void DrawBars(float xOffset, float yOffset, float width)
- {
- if (_sortedProfileData.Count != 0)
- {
- long maxAverage;
- long maxTotal;
-
- int verticalIndex = 0;
- float barHeight = (LineHeight - LinePadding) / 3.0f;
-
- // Get max values
- long maxInstant = maxAverage = maxTotal = 0;
- foreach (KeyValuePair kvp in _sortedProfileData)
- {
- maxInstant = Math.Max(maxInstant, kvp.Value.Instant);
- maxAverage = Math.Max(maxAverage, kvp.Value.AverageTime);
- maxTotal = Math.Max(maxTotal, kvp.Value.TotalTime);
- }
-
- GL.Enable(EnableCap.ScissorTest);
- GL.Begin(PrimitiveType.Triangles);
- foreach (var entry in _sortedProfileData)
- {
- // Instant
- GL.Color3(Color.Blue);
- float bottom = GetLineY(yOffset, LineHeight, LinePadding, true, verticalIndex++);
- float top = bottom + barHeight;
- float right = (float)entry.Value.Instant / maxInstant * width + xOffset;
-
- // Skip rendering out of bounds bars
- if (top < 0 || bottom > Height)
- continue;
-
- GL.Vertex2(xOffset, bottom);
- GL.Vertex2(xOffset, top);
- GL.Vertex2(right, top);
-
- GL.Vertex2(right, top);
- GL.Vertex2(right, bottom);
- GL.Vertex2(xOffset, bottom);
-
- // Average
- GL.Color3(Color.Green);
- top += barHeight;
- bottom += barHeight;
- right = (float)entry.Value.AverageTime / maxAverage * width + xOffset;
-
- GL.Vertex2(xOffset, bottom);
- GL.Vertex2(xOffset, top);
- GL.Vertex2(right, top);
-
- GL.Vertex2(right, top);
- GL.Vertex2(right, bottom);
- GL.Vertex2(xOffset, bottom);
-
- // Total
- GL.Color3(Color.Red);
- top += barHeight;
- bottom += barHeight;
- right = (float)entry.Value.TotalTime / maxTotal * width + xOffset;
-
- GL.Vertex2(xOffset, bottom);
- GL.Vertex2(xOffset, top);
- GL.Vertex2(right, top);
-
- GL.Vertex2(right, top);
- GL.Vertex2(right, bottom);
- GL.Vertex2(xOffset, bottom);
- }
-
- GL.End();
- GL.Disable(EnableCap.ScissorTest);
- }
- }
- }
-}
diff --git a/Ryujinx.Profiler/UI/ProfileWindowGraph.cs b/Ryujinx.Profiler/UI/ProfileWindowGraph.cs
deleted file mode 100644
index 6a4a52a998..0000000000
--- a/Ryujinx.Profiler/UI/ProfileWindowGraph.cs
+++ /dev/null
@@ -1,151 +0,0 @@
-using System;
-using OpenTK;
-using OpenTK.Graphics.OpenGL;
-using Ryujinx.Common;
-
-namespace Ryujinx.Profiler.UI
-{
- public partial class ProfileWindow
- {
- // Color index equal to timing flag type as int
- private Color[] _timingFlagColors = new[]
- {
- new Color(150, 25, 25, 50), // FrameSwap = 0
- new Color(25, 25, 150, 50), // SystemFrame = 1
- };
-
- private TimingFlag[] _timingFlags;
-
- private const float GraphMoveSpeed = 40000;
- private const float GraphZoomSpeed = 50;
-
- private float _graphZoom = 1;
- private float _graphPosition = 0;
-
- private void DrawGraph(float xOffset, float yOffset, float width)
- {
- if (_sortedProfileData.Count != 0)
- {
- int left, right;
- float top, bottom;
-
- int verticalIndex = 0;
- float graphRight = xOffset + width;
- float barHeight = (LineHeight - LinePadding);
- long history = Profile.HistoryLength;
- double timeWidthTicks = history / (double)_graphZoom;
- long graphPositionTicks = (long)(_graphPosition * PerformanceCounter.TicksPerMillisecond);
- long ticksPerPixel = (long)(timeWidthTicks / width);
-
- // Reset start point if out of bounds
- if (timeWidthTicks + graphPositionTicks > history)
- {
- graphPositionTicks = history - (long)timeWidthTicks;
- _graphPosition = (float)graphPositionTicks / PerformanceCounter.TicksPerMillisecond;
- }
-
- graphPositionTicks = _captureTime - graphPositionTicks;
-
- GL.Enable(EnableCap.ScissorTest);
-
- // Draw timing flags
- if (_displayFlags)
- {
- TimingFlagType prevType = TimingFlagType.Count;
-
- GL.Enable(EnableCap.Blend);
- GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);
-
- GL.Begin(PrimitiveType.Lines);
- foreach (TimingFlag timingFlag in _timingFlags)
- {
- if (prevType != timingFlag.FlagType)
- {
- prevType = timingFlag.FlagType;
- GL.Color4(_timingFlagColors[(int)prevType]);
- }
-
- int x = (int)(graphRight - ((graphPositionTicks - timingFlag.Timestamp) / timeWidthTicks) * width);
- GL.Vertex2(x, 0);
- GL.Vertex2(x, Height);
- }
- GL.End();
- GL.Disable(EnableCap.Blend);
- }
-
- // Draw bars
- GL.Begin(PrimitiveType.Triangles);
- foreach (var entry in _sortedProfileData)
- {
- long furthest = 0;
-
- bottom = GetLineY(yOffset, LineHeight, LinePadding, true, verticalIndex);
- top = bottom + barHeight;
-
- // Skip rendering out of bounds bars
- if (top < 0 || bottom > Height)
- {
- verticalIndex++;
- continue;
- }
-
-
- GL.Color3(Color.Green);
- foreach (Timestamp timestamp in entry.Value.GetAllTimestamps())
- {
- // Skip drawing multiple timestamps on same pixel
- if (timestamp.EndTime < furthest)
- continue;
- furthest = timestamp.EndTime + ticksPerPixel;
-
- left = (int)(graphRight - ((graphPositionTicks - timestamp.BeginTime) / timeWidthTicks) * width);
- right = (int)(graphRight - ((graphPositionTicks - timestamp.EndTime) / timeWidthTicks) * width);
-
- // Make sure width is at least 1px
- right = Math.Max(left + 1, right);
-
- GL.Vertex2(left, bottom);
- GL.Vertex2(left, top);
- GL.Vertex2(right, top);
-
- GL.Vertex2(right, top);
- GL.Vertex2(right, bottom);
- GL.Vertex2(left, bottom);
- }
-
- // Currently capturing timestamp
- GL.Color3(Color.Red);
- long entryBegin = entry.Value.BeginTime;
- if (entryBegin != -1)
- {
- left = (int)(graphRight - ((graphPositionTicks - entryBegin) / timeWidthTicks) * width);
-
- // Make sure width is at least 1px
- left = Math.Min(left - 1, (int)graphRight);
-
- GL.Vertex2(left, bottom);
- GL.Vertex2(left, top);
- GL.Vertex2(graphRight, top);
-
- GL.Vertex2(graphRight, top);
- GL.Vertex2(graphRight, bottom);
- GL.Vertex2(left, bottom);
- }
-
- verticalIndex++;
- }
-
- GL.End();
- GL.Disable(EnableCap.ScissorTest);
-
- string label = $"-{MathF.Round(_graphPosition, 2)} ms";
-
- // Dummy draw for measure
- float labelWidth = _fontService.DrawText(label, 0, 0, LineHeight, false);
- _fontService.DrawText(label, graphRight - labelWidth - LinePadding, FilterHeight + LinePadding, LineHeight);
-
- _fontService.DrawText($"-{MathF.Round((float)((timeWidthTicks / PerformanceCounter.TicksPerMillisecond) + _graphPosition), 2)} ms", xOffset + LinePadding, FilterHeight + LinePadding, LineHeight);
- }
- }
- }
-}
diff --git a/Ryujinx.Profiler/UI/ProfileWindowManager.cs b/Ryujinx.Profiler/UI/ProfileWindowManager.cs
deleted file mode 100644
index 1360302933..0000000000
--- a/Ryujinx.Profiler/UI/ProfileWindowManager.cs
+++ /dev/null
@@ -1,95 +0,0 @@
-using System.Threading;
-using OpenTK;
-using OpenTK.Input;
-using Ryujinx.Common;
-
-namespace Ryujinx.Profiler.UI
-{
- public class ProfileWindowManager
- {
- private ProfileWindow _window;
- private Thread _profileThread;
- private Thread _renderThread;
- private bool _profilerRunning;
-
- // Timing
- private double _prevTime;
-
- public ProfileWindowManager()
- {
- if (Profile.ProfilingEnabled())
- {
- _profilerRunning = true;
- _prevTime = 0;
- _profileThread = new Thread(ProfileLoop)
- {
- Name = "Profiler.ProfileThread"
- };
- _profileThread.Start();
- }
- }
-
- public void ToggleVisible()
- {
- if (Profile.ProfilingEnabled())
- {
- _window.ToggleVisible();
- }
- }
-
- public void Close()
- {
- if (_window != null)
- {
- _profilerRunning = false;
- _window.Close();
- _window.Dispose();
- }
-
- _window = null;
- }
-
- public void UpdateKeyInput(KeyboardState keyboard)
- {
- if (Profile.Controls.TogglePressed(keyboard))
- {
- ToggleVisible();
- }
- Profile.Controls.SetPrevKeyboardState(keyboard);
- }
-
- private void ProfileLoop()
- {
- using (_window = new ProfileWindow())
- {
- // Create thread for render loop
- _renderThread = new Thread(RenderLoop)
- {
- Name = "Profiler.RenderThread"
- };
- _renderThread.Start();
-
- while (_profilerRunning)
- {
- double time = (double)PerformanceCounter.ElapsedTicks / PerformanceCounter.TicksPerSecond;
- _window.Update(new FrameEventArgs(time - _prevTime));
- _prevTime = time;
-
- // Sleep to be less taxing, update usually does very little
- Thread.Sleep(1);
- }
- }
- }
-
- private void RenderLoop()
- {
- _window.Context.MakeCurrent(_window.WindowInfo);
-
- while (_profilerRunning)
- {
- _window.Draw();
- Thread.Sleep(1);
- }
- }
- }
-}
diff --git a/Ryujinx.Profiler/UI/SharpFontHelpers/FontService.cs b/Ryujinx.Profiler/UI/SharpFontHelpers/FontService.cs
deleted file mode 100644
index 32846977e9..0000000000
--- a/Ryujinx.Profiler/UI/SharpFontHelpers/FontService.cs
+++ /dev/null
@@ -1,257 +0,0 @@
-using System;
-using System.IO;
-using System.Runtime.InteropServices;
-using OpenTK;
-using OpenTK.Graphics.OpenGL;
-using SharpFont;
-
-namespace Ryujinx.Profiler.UI.SharpFontHelpers
-{
- public class FontService
- {
- private struct CharacterInfo
- {
- public float Left;
- public float Right;
- public float Top;
- public float Bottom;
-
- public int Width;
- public float Height;
-
- public float AspectRatio;
-
- public float BearingX;
- public float BearingY;
- public float Advance;
- }
-
- private const int SheetWidth = 1024;
- private const int SheetHeight = 512;
- private int ScreenWidth, ScreenHeight;
- private int CharacterTextureSheet;
- private CharacterInfo[] characters;
-
- public Color fontColor { get; set; } = Color.Black;
-
- private string GetFontPath()
- {
- string fontFolder = Environment.GetFolderPath(Environment.SpecialFolder.Fonts);
-
- // Only uses Arial, add more fonts here if wanted
- string path = Path.Combine(fontFolder, "arial.ttf");
- if (File.Exists(path))
- {
- return path;
- }
-
- throw new Exception($"Profiler exception. Required font Courier New or Arial not installed to {fontFolder}");
- }
-
- public void InitializeTextures()
- {
- // Create and init some vars
- uint[] rawCharacterSheet = new uint[SheetWidth * SheetHeight];
- int x;
- int y;
- int lineOffset;
- int maxHeight;
-
- x = y = lineOffset = maxHeight = 0;
- characters = new CharacterInfo[94];
-
- // Get font
- var font = new FontFace(File.OpenRead(GetFontPath()));
-
- // Update raw data for each character
- for (int i = 0; i < 94; i++)
- {
- var surface = RenderSurface((char)(i + 33), font, out float xBearing, out float yBearing, out float advance);
-
- characters[i] = UpdateTexture(surface, ref rawCharacterSheet, ref x, ref y, ref lineOffset);
- characters[i].BearingX = xBearing;
- characters[i].BearingY = yBearing;
- characters[i].Advance = advance;
-
- if (maxHeight < characters[i].Height)
- maxHeight = (int)characters[i].Height;
- }
-
- // Fix height for characters shorter than line height
- for (int i = 0; i < 94; i++)
- {
- characters[i].BearingX /= characters[i].Width;
- characters[i].BearingY /= maxHeight;
- characters[i].Advance /= characters[i].Width;
- characters[i].Height /= maxHeight;
- characters[i].AspectRatio = (float)characters[i].Width / maxHeight;
- }
-
- // Convert raw data into texture
- CharacterTextureSheet = GL.GenTexture();
- GL.BindTexture(TextureTarget.Texture2D, CharacterTextureSheet);
-
- GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
- GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
- GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Clamp);
- GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Clamp);
-
- GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, SheetWidth, SheetHeight, 0, PixelFormat.Rgba, PixelType.UnsignedInt8888, rawCharacterSheet);
-
- GL.BindTexture(TextureTarget.Texture2D, 0);
- }
-
- public void UpdateScreenHeight(int height)
- {
- ScreenHeight = height;
- }
-
- public float DrawText(string text, float x, float y, float height, bool draw = true)
- {
- float originalX = x;
-
- // Skip out of bounds draw
- if (y < height * -2 || y > ScreenHeight + height * 2)
- {
- draw = false;
- }
-
- if (draw)
- {
- // Use font map texture
- GL.BindTexture(TextureTarget.Texture2D, CharacterTextureSheet);
-
- // Enable blending and textures
- GL.Enable(EnableCap.Texture2D);
- GL.Enable(EnableCap.Blend);
- GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);
-
- // Draw all characters
- GL.Begin(PrimitiveType.Triangles);
- GL.Color4(fontColor);
- }
-
- for (int i = 0; i < text.Length; i++)
- {
- if (text[i] == ' ')
- {
- x += height / 4;
- continue;
- }
-
- CharacterInfo charInfo = characters[text[i] - 33];
- float width = (charInfo.AspectRatio * height);
- x += (charInfo.BearingX * charInfo.AspectRatio) * width;
- float right = x + width;
- if (draw)
- {
- DrawChar(charInfo, x, right, y + height * (charInfo.Height - charInfo.BearingY), y - height * charInfo.BearingY);
- }
- x = right + charInfo.Advance * charInfo.AspectRatio + 1;
- }
-
- if (draw)
- {
- GL.End();
-
- // Cleanup for caller
- GL.BindTexture(TextureTarget.Texture2D, 0);
- GL.Disable(EnableCap.Texture2D);
- GL.Disable(EnableCap.Blend);
- }
-
- // Return width of rendered text
- return x - originalX;
- }
-
- private void DrawChar(CharacterInfo charInfo, float left, float right, float top, float bottom)
- {
- GL.TexCoord2(charInfo.Left, charInfo.Bottom); GL.Vertex2(left, bottom);
- GL.TexCoord2(charInfo.Left, charInfo.Top); GL.Vertex2(left, top);
- GL.TexCoord2(charInfo.Right, charInfo.Top); GL.Vertex2(right, top);
-
- GL.TexCoord2(charInfo.Right, charInfo.Top); GL.Vertex2(right, top);
- GL.TexCoord2(charInfo.Right, charInfo.Bottom); GL.Vertex2(right, bottom);
- GL.TexCoord2(charInfo.Left, charInfo.Bottom); GL.Vertex2(left, bottom);
- }
-
- public unsafe Surface RenderSurface(char c, FontFace font, out float xBearing, out float yBearing, out float advance)
- {
- var glyph = font.GetGlyph(c, 64);
- xBearing = glyph.HorizontalMetrics.Bearing.X;
- yBearing = glyph.RenderHeight - glyph.HorizontalMetrics.Bearing.Y;
- advance = glyph.HorizontalMetrics.Advance;
-
- var surface = new Surface
- {
- Bits = Marshal.AllocHGlobal(glyph.RenderWidth * glyph.RenderHeight),
- Width = glyph.RenderWidth,
- Height = glyph.RenderHeight,
- Pitch = glyph.RenderWidth
- };
-
- var stuff = (byte*)surface.Bits;
- for (int i = 0; i < surface.Width * surface.Height; i++)
- *stuff++ = 0;
-
- glyph.RenderTo(surface);
-
- return surface;
- }
-
- private CharacterInfo UpdateTexture(Surface surface, ref uint[] rawCharMap, ref int posX, ref int posY, ref int lineOffset)
- {
- int width = surface.Width;
- int height = surface.Height;
- int len = width * height;
- byte[] data = new byte[len];
-
- // Get character bitmap
- Marshal.Copy(surface.Bits, data, 0, len);
-
- // Find a slot
- if (posX + width > SheetWidth)
- {
- posX = 0;
- posY += lineOffset;
- lineOffset = 0;
- }
-
- // Update lineOffset
- if (lineOffset < height)
- {
- lineOffset = height + 1;
- }
-
- // Copy char to sheet
- for (int y = 0; y < height; y++)
- {
- int destOffset = (y + posY) * SheetWidth + posX;
- int sourceOffset = y * width;
-
- for (int x = 0; x < width; x++)
- {
- rawCharMap[destOffset + x] = (uint)((0xFFFFFF << 8) | data[sourceOffset + x]);
- }
- }
-
- // Generate character info
- CharacterInfo charInfo = new CharacterInfo()
- {
- Left = (float)posX / SheetWidth,
- Right = (float)(posX + width) / SheetWidth,
- Top = (float)(posY - 1) / SheetHeight,
- Bottom = (float)(posY + height) / SheetHeight,
- Width = width,
- Height = height,
- };
-
- // Update x
- posX += width + 1;
-
- // Give the memory back
- Marshal.FreeHGlobal(surface.Bits);
- return charInfo;
- }
- }
-}
\ No newline at end of file
diff --git a/Ryujinx.ShaderTools/Ryujinx.ShaderTools.csproj b/Ryujinx.ShaderTools/Ryujinx.ShaderTools.csproj
index 2f9c777278..10d66cd281 100644
--- a/Ryujinx.ShaderTools/Ryujinx.ShaderTools.csproj
+++ b/Ryujinx.ShaderTools/Ryujinx.ShaderTools.csproj
@@ -12,12 +12,12 @@
- TRACE;USE_PROFILING
+ TRACE;USE_DEBUGGING
true
- TRACE;USE_PROFILING
+ TRACE;USE_DEBUGGING
false
diff --git a/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj b/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj
index 36310f3d2a..f99f504a75 100644
--- a/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj
+++ b/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj
@@ -12,12 +12,12 @@
- TRACE;USE_PROFILING
+ TRACE;USE_DEBUGGING
true
- TRACE;USE_PROFILING
+ TRACE;USE_DEBUGGING
false
diff --git a/Ryujinx.Tests/Ryujinx.Tests.csproj b/Ryujinx.Tests/Ryujinx.Tests.csproj
index 83ec2e9647..b256cc6c81 100644
--- a/Ryujinx.Tests/Ryujinx.Tests.csproj
+++ b/Ryujinx.Tests/Ryujinx.Tests.csproj
@@ -17,12 +17,12 @@
- TRACE;USE_PROFILING
+ TRACE;USE_DEBUGGING
true
- TRACE;USE_PROFILING
+ TRACE;USE_DEBUGGING
false
diff --git a/Ryujinx.sln b/Ryujinx.sln
index 4ad74077cf..f023368ba8 100644
--- a/Ryujinx.sln
+++ b/Ryujinx.sln
@@ -10,9 +10,6 @@ EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Tests.Unicorn", "Ryujinx.Tests.Unicorn\Ryujinx.Tests.Unicorn.csproj", "{D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE", "Ryujinx.HLE\Ryujinx.HLE.csproj", "{CB92CFF9-1D62-4D4F-9E88-8130EF61E351}"
- ProjectSection(ProjectDependencies) = postProject
- {4E69B67F-8CA7-42CF-A9E1-CCB0915DFB34} = {4E69B67F-8CA7-42CF-A9E1-CCB0915DFB34}
- EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio", "Ryujinx.Audio\Ryujinx.Audio.csproj", "{5C1D818E-682A-46A5-9D54-30006E26C270}"
EndProject
@@ -22,8 +19,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Luea", "Ryujinx.LLE\Luea.cs
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Common", "Ryujinx.Common\Ryujinx.Common.csproj", "{5FD4E4F6-8928-4B3C-BE07-28A675C17226}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Profiler", "Ryujinx.Profiler\Ryujinx.Profiler.csproj", "{4E69B67F-8CA7-42CF-A9E1-CCB0915DFB34}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ARMeilleure", "ARMeilleure\ARMeilleure.csproj", "{ABF09A5E-2D8B-4B6F-A51D-5CE414DDB15A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Gpu", "Ryujinx.Graphics.Gpu\Ryujinx.Graphics.Gpu.csproj", "{ADA7EA87-0D63-4D97-9433-922A2124401F}"
@@ -37,6 +32,7 @@ EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Shader", "Ryujinx.Graphics.Shader\Ryujinx.Graphics.Shader.csproj", "{03B955CD-AD84-4B93-AAA7-BF17923BBAA5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Graphics.Nvdec", "Ryujinx.Graphics.Nvdec\Ryujinx.Graphics.Nvdec.csproj", "{85A0FA56-DC01-4A42-8808-70DAC76BD66D}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Debugger", "Ryujinx.Debugger\Ryujinx.Debugger.csproj", "{2E02B7F3-245E-43B1-AE5B-44167A0FDA20}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -110,14 +106,6 @@ Global
{5FD4E4F6-8928-4B3C-BE07-28A675C17226}.Profile Release|Any CPU.Build.0 = Profile Release|Any CPU
{5FD4E4F6-8928-4B3C-BE07-28A675C17226}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5FD4E4F6-8928-4B3C-BE07-28A675C17226}.Release|Any CPU.Build.0 = Release|Any CPU
- {4E69B67F-8CA7-42CF-A9E1-CCB0915DFB34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {4E69B67F-8CA7-42CF-A9E1-CCB0915DFB34}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {4E69B67F-8CA7-42CF-A9E1-CCB0915DFB34}.Profile Debug|Any CPU.ActiveCfg = Profile Debug|Any CPU
- {4E69B67F-8CA7-42CF-A9E1-CCB0915DFB34}.Profile Debug|Any CPU.Build.0 = Profile Debug|Any CPU
- {4E69B67F-8CA7-42CF-A9E1-CCB0915DFB34}.Profile Release|Any CPU.ActiveCfg = Profile Release|Any CPU
- {4E69B67F-8CA7-42CF-A9E1-CCB0915DFB34}.Profile Release|Any CPU.Build.0 = Profile Release|Any CPU
- {4E69B67F-8CA7-42CF-A9E1-CCB0915DFB34}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {4E69B67F-8CA7-42CF-A9E1-CCB0915DFB34}.Release|Any CPU.Build.0 = Release|Any CPU
{ABF09A5E-2D8B-4B6F-A51D-5CE414DDB15A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ABF09A5E-2D8B-4B6F-A51D-5CE414DDB15A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ABF09A5E-2D8B-4B6F-A51D-5CE414DDB15A}.Profile Debug|Any CPU.ActiveCfg = Debug|Any CPU
@@ -174,6 +162,14 @@ Global
{85A0FA56-DC01-4A42-8808-70DAC76BD66D}.Profile Release|Any CPU.Build.0 = Release|Any CPU
{85A0FA56-DC01-4A42-8808-70DAC76BD66D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{85A0FA56-DC01-4A42-8808-70DAC76BD66D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2E02B7F3-245E-43B1-AE5B-44167A0FDA20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2E02B7F3-245E-43B1-AE5B-44167A0FDA20}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2E02B7F3-245E-43B1-AE5B-44167A0FDA20}.Profile Debug|Any CPU.ActiveCfg = Profile Debug|Any CPU
+ {2E02B7F3-245E-43B1-AE5B-44167A0FDA20}.Profile Debug|Any CPU.Build.0 = Profile Debug|Any CPU
+ {2E02B7F3-245E-43B1-AE5B-44167A0FDA20}.Profile Release|Any CPU.ActiveCfg = Profile Release|Any CPU
+ {2E02B7F3-245E-43B1-AE5B-44167A0FDA20}.Profile Release|Any CPU.Build.0 = Profile Release|Any CPU
+ {2E02B7F3-245E-43B1-AE5B-44167A0FDA20}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2E02B7F3-245E-43B1-AE5B-44167A0FDA20}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/Ryujinx/Program.cs b/Ryujinx/Program.cs
index 9bab74a265..5ce33a9dd2 100644
--- a/Ryujinx/Program.cs
+++ b/Ryujinx/Program.cs
@@ -1,7 +1,7 @@
using Gtk;
using Ryujinx.Common.Logging;
using Ryujinx.Configuration;
-using Ryujinx.Profiler;
+using Ryujinx.Debugger.Profiler;
using Ryujinx.Ui;
using System;
using System.IO;
diff --git a/Ryujinx/Ryujinx.csproj b/Ryujinx/Ryujinx.csproj
index 1ff9800155..cc8e6d5450 100644
--- a/Ryujinx/Ryujinx.csproj
+++ b/Ryujinx/Ryujinx.csproj
@@ -9,12 +9,12 @@
- TRACE;USE_PROFILING
+ TRACE;USE_DEBUGGING
true
- TRACE;USE_PROFILING
+ TRACE;USE_DEBUGGING
false
@@ -72,7 +72,7 @@
-
+
@@ -80,8 +80,8 @@
+
-
diff --git a/Ryujinx/Ui/GLScreen.cs b/Ryujinx/Ui/GLScreen.cs
index 295cf22f49..5e83458aef 100644
--- a/Ryujinx/Ui/GLScreen.cs
+++ b/Ryujinx/Ui/GLScreen.cs
@@ -5,8 +5,6 @@ using Ryujinx.Configuration;
using Ryujinx.Graphics.OpenGL;
using Ryujinx.HLE;
using Ryujinx.HLE.Input;
-using Ryujinx.Profiler.UI;
-using Ryujinx.Ui;
using System;
using System.Threading;
@@ -41,10 +39,6 @@ namespace Ryujinx.Ui
private string _newTitle;
-#if USE_PROFILING
- private ProfileWindowManager _profileWindow;
-#endif
-
public GlScreen(Switch device)
: base(1280, 720,
new GraphicsMode(), "Ryujinx", 0,
@@ -65,11 +59,6 @@ namespace Ryujinx.Ui
Location = new Point(
(DisplayDevice.Default.Width / 2) - (Width / 2),
(DisplayDevice.Default.Height / 2) - (Height / 2));
-
-#if USE_PROFILING
- // Start profile window, it will handle itself from there
- _profileWindow = new ProfileWindowManager();
-#endif
}
private void RenderLoop()
@@ -171,11 +160,6 @@ namespace Ryujinx.Ui
{
KeyboardState keyboard = _keyboard.Value;
-#if USE_PROFILING
- // Profiler input, lets the profiler get access to the main windows keyboard state
- _profileWindow.UpdateKeyInput(keyboard);
-#endif
-
// Normal Input
currentHotkeyButtons = KeyboardControls.GetHotkeyButtons(ConfigurationState.Instance.Hid.KeyboardControls, keyboard);
currentButton = KeyboardControls.GetButtons(ConfigurationState.Instance.Hid.KeyboardControls, keyboard);
@@ -330,10 +314,6 @@ namespace Ryujinx.Ui
protected override void OnUnload(EventArgs e)
{
-#if USE_PROFILING
- _profileWindow.Close();
-#endif
-
_renderThread.Join();
base.OnUnload(e);
diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs
index 61c59d356c..84c736befc 100644
--- a/Ryujinx/Ui/MainWindow.cs
+++ b/Ryujinx/Ui/MainWindow.cs
@@ -3,11 +3,12 @@ using JsonPrettyPrinterPlus;
using Ryujinx.Audio;
using Ryujinx.Common.Logging;
using Ryujinx.Configuration;
+using Ryujinx.Debugger.Profiler;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.OpenGL;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.FileSystem.Content;
-using Ryujinx.Profiler;
+using Ryujinx.HLE.FileSystem;
using System;
using System.Diagnostics;
using System.IO;
@@ -36,9 +37,12 @@ namespace Ryujinx.Ui
private static bool _updatingGameTable;
private static bool _gameLoaded;
private static bool _ending;
+ private static bool _debuggerOpened;
private static TreeView _treeView;
+ private static Debugger.Debugger _debugger;
+
#pragma warning disable CS0649
#pragma warning disable IDE0044
[GUI] Window _mainWin;
@@ -61,6 +65,8 @@ namespace Ryujinx.Ui
[GUI] Label _progressLabel;
[GUI] Label _firmwareVersionLabel;
[GUI] LevelBar _progressBar;
+ [GUI] MenuItem _openDebugger;
+ [GUI] MenuItem _toolsMenu;
#pragma warning restore CS0649
#pragma warning restore IDE0044
@@ -118,6 +124,13 @@ namespace Ryujinx.Ui
if (ConfigurationState.Instance.Ui.GuiColumns.FileSizeColumn) _fileSizeToggle.Active = true;
if (ConfigurationState.Instance.Ui.GuiColumns.PathColumn) _pathToggle.Active = true;
+#if USE_DEBUGGING
+ _debugger = new Debugger.Debugger();
+ _openDebugger.Activated += _openDebugger_Opened;
+#else
+ _openDebugger.Visible = false;
+#endif
+
_gameTable.Model = _tableStore = new ListStore(
typeof(bool),
typeof(Gdk.Pixbuf),
@@ -141,6 +154,36 @@ namespace Ryujinx.Ui
Task.Run(RefreshFirmwareLabel);
}
+#if USE_DEBUGGING
+ private void _openDebugger_Opened(object sender, EventArgs e)
+ {
+ if (_debuggerOpened)
+ {
+ return;
+ }
+
+ Window debugWindow = new Window("Debugger");
+
+ debugWindow.SetSizeRequest(1280, 640);
+ debugWindow.Child = _debugger.Widget;
+ debugWindow.DeleteEvent += DebugWindow_DeleteEvent;
+ debugWindow.ShowAll();
+
+ _debugger.Enable();
+
+ _debuggerOpened = true;
+ }
+
+ private void DebugWindow_DeleteEvent(object o, DeleteEventArgs args)
+ {
+ _debuggerOpened = false;
+
+ _debugger.Disable();
+
+ (_debugger.Widget.Parent as Window)?.Remove(_debugger.Widget);
+ }
+#endif
+
internal static void ApplyTheme()
{
if (!ConfigurationState.Instance.Ui.EnableCustomTheme)
@@ -307,7 +350,15 @@ namespace Ryujinx.Ui
#if MACOS_BUILD
CreateGameWindow(device);
#else
- new Thread(() => CreateGameWindow(device)).Start();
+ var windowThread = new Thread(() =>
+ {
+ CreateGameWindow(device);
+ })
+ {
+ Name = "GUI.WindowThread"
+ };
+
+ windowThread.Start();
#endif
_gameLoaded = true;
@@ -366,6 +417,11 @@ namespace Ryujinx.Ui
private void End(HLE.Switch device)
{
+
+#if USE_DEBUGGING
+ _debugger.Dispose();
+#endif
+
if (_ending)
{
return;
diff --git a/Ryujinx/Ui/MainWindow.glade b/Ryujinx/Ui/MainWindow.glade
index 8e2eab9391..d3cdc59346 100644
--- a/Ryujinx/Ui/MainWindow.glade
+++ b/Ryujinx/Ui/MainWindow.glade
@@ -1,5 +1,5 @@
-
+
@@ -8,6 +8,9 @@
center
1280
750
+
+
+
True
@@ -255,7 +258,7 @@
-
+
+
+
@@ -499,8 +510,5 @@
-
-
-