diff --git a/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs b/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs
index 13dad62ce0..ae3fa4937f 100644
--- a/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs
+++ b/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs
@@ -1,5 +1,6 @@
 using System.Collections.Generic;
 using System.IO;
+using Ryujinx.Common.Configuration;
 using Ryujinx.Common.Configuration.Hid;
 using Ryujinx.Common.Logging;
 using Ryujinx.Common.Utilities;
@@ -13,7 +14,7 @@ namespace Ryujinx.Configuration
         /// <summary>
         /// The current version of the file format
         /// </summary>
-        public const int CurrentVersion = 11;
+        public const int CurrentVersion = 12;
 
         public int Version { get; set; }
 
@@ -77,6 +78,11 @@ namespace Ryujinx.Configuration
         /// </summary>
         public LogClass[] LoggingFilteredClasses { get; set; }
 
+        /// <summary>
+        /// Change Graphics API debug log level
+        /// </summary>
+        public GraphicsDebugLevel LoggingGraphicsDebugLevel { get; set; }
+
         /// <summary>
         /// Enables or disables logging to a file on disk
         /// </summary>
diff --git a/Ryujinx.Common/Configuration/ConfigurationState.cs b/Ryujinx.Common/Configuration/ConfigurationState.cs
index 3149f250f1..dc9dd65991 100644
--- a/Ryujinx.Common/Configuration/ConfigurationState.cs
+++ b/Ryujinx.Common/Configuration/ConfigurationState.cs
@@ -1,4 +1,5 @@
 using Ryujinx.Common;
+using Ryujinx.Common.Configuration;
 using Ryujinx.Common.Configuration.Hid;
 using Ryujinx.Common.Logging;
 using Ryujinx.Configuration.Hid;
@@ -141,17 +142,23 @@ namespace Ryujinx.Configuration
             /// </summary>
             public ReactiveObject<bool> EnableFileLog { get; private set; }
 
+            /// <summary>
+            /// Controls which OpenGL log messages are recorded in the log
+            /// </summary>
+            public ReactiveObject<GraphicsDebugLevel> GraphicsDebugLevel { get; private set; }
+
             public LoggerSection()
             {
-                EnableDebug       = new ReactiveObject<bool>();
-                EnableStub        = new ReactiveObject<bool>();
-                EnableInfo        = new ReactiveObject<bool>();
-                EnableWarn        = new ReactiveObject<bool>();
-                EnableError       = new ReactiveObject<bool>();
-                EnableGuest       = new ReactiveObject<bool>();
-                EnableFsAccessLog = new ReactiveObject<bool>();
-                FilteredClasses   = new ReactiveObject<LogClass[]>();
-                EnableFileLog     = new ReactiveObject<bool>();
+                EnableDebug        = new ReactiveObject<bool>();
+                EnableStub         = new ReactiveObject<bool>();
+                EnableInfo         = new ReactiveObject<bool>();
+                EnableWarn         = new ReactiveObject<bool>();
+                EnableError        = new ReactiveObject<bool>();
+                EnableGuest        = new ReactiveObject<bool>();
+                EnableFsAccessLog  = new ReactiveObject<bool>();
+                FilteredClasses    = new ReactiveObject<LogClass[]>();
+                EnableFileLog      = new ReactiveObject<bool>();
+                GraphicsDebugLevel = new ReactiveObject<GraphicsDebugLevel>();
             }
         }
 
@@ -378,6 +385,7 @@ namespace Ryujinx.Configuration
                 LoggingEnableGuest        = Logger.EnableGuest,
                 LoggingEnableFsAccessLog  = Logger.EnableFsAccessLog,
                 LoggingFilteredClasses    = Logger.FilteredClasses,
+                LoggingGraphicsDebugLevel = Logger.GraphicsDebugLevel,
                 EnableFileLog             = Logger.EnableFileLog,
                 SystemLanguage            = System.Language,
                 SystemRegion              = System.Region,
@@ -436,6 +444,7 @@ namespace Ryujinx.Configuration
             Logger.EnableGuest.Value               = true;
             Logger.EnableFsAccessLog.Value         = false;
             Logger.FilteredClasses.Value           = new LogClass[] { };
+            Logger.GraphicsDebugLevel.Value        = GraphicsDebugLevel.None;
             Logger.EnableFileLog.Value             = true;
             System.Language.Value                  = Language.AmericanEnglish;
             System.Region.Value                    = Region.USA;
@@ -678,6 +687,15 @@ namespace Ryujinx.Configuration
                 configurationFileUpdated = true;
             }
 
+            if (configurationFileFormat.Version < 12)
+            {
+                Common.Logging.Logger.PrintWarning(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 12.");
+
+                configurationFileFormat.LoggingGraphicsDebugLevel = GraphicsDebugLevel.None;
+
+                configurationFileUpdated = true;
+            }
+
             List<InputConfig> inputConfig = new List<InputConfig>();
             inputConfig.AddRange(configurationFileFormat.ControllerConfig);
             inputConfig.AddRange(configurationFileFormat.KeyboardConfig);
@@ -694,6 +712,7 @@ namespace Ryujinx.Configuration
             Logger.EnableGuest.Value               = configurationFileFormat.LoggingEnableGuest;
             Logger.EnableFsAccessLog.Value         = configurationFileFormat.LoggingEnableFsAccessLog;
             Logger.FilteredClasses.Value           = configurationFileFormat.LoggingFilteredClasses;
+            Logger.GraphicsDebugLevel.Value        = configurationFileFormat.LoggingGraphicsDebugLevel;
             Logger.EnableFileLog.Value             = configurationFileFormat.EnableFileLog;
             System.Language.Value                  = configurationFileFormat.SystemLanguage;
             System.Region.Value                    = configurationFileFormat.SystemRegion;
diff --git a/Ryujinx.Common/Configuration/GraphicsDebugLevel.cs b/Ryujinx.Common/Configuration/GraphicsDebugLevel.cs
new file mode 100644
index 0000000000..1bef4a7e90
--- /dev/null
+++ b/Ryujinx.Common/Configuration/GraphicsDebugLevel.cs
@@ -0,0 +1,10 @@
+namespace Ryujinx.Common.Configuration
+{
+    public enum GraphicsDebugLevel
+    {
+        None,
+        Error,
+        Performance,
+        All
+    }
+}
diff --git a/Ryujinx.Graphics.GAL/IRenderer.cs b/Ryujinx.Graphics.GAL/IRenderer.cs
index 6fd3feba60..fec8d3bea5 100644
--- a/Ryujinx.Graphics.GAL/IRenderer.cs
+++ b/Ryujinx.Graphics.GAL/IRenderer.cs
@@ -1,3 +1,4 @@
+using Ryujinx.Common.Configuration;
 using Ryujinx.Graphics.Shader;
 using System;
 
@@ -32,6 +33,6 @@ namespace Ryujinx.Graphics.GAL
 
         void ResetCounter(CounterType type);
 
-        void Initialize();
+        void Initialize(GraphicsDebugLevel logLevel);
     }
 }
diff --git a/Ryujinx.Graphics.OpenGL/Debugger.cs b/Ryujinx.Graphics.OpenGL/Debugger.cs
index ff9fcd855a..9d0a1f59b6 100644
--- a/Ryujinx.Graphics.OpenGL/Debugger.cs
+++ b/Ryujinx.Graphics.OpenGL/Debugger.cs
@@ -1,7 +1,9 @@
 using OpenTK.Graphics.OpenGL;
+using Ryujinx.Common.Configuration;
 using Ryujinx.Common.Logging;
 using System;
 using System.Runtime.InteropServices;
+using System.Threading;
 
 namespace Ryujinx.Graphics.OpenGL
 {
@@ -9,15 +11,43 @@ namespace Ryujinx.Graphics.OpenGL
     {
         private static DebugProc _debugCallback;
 
-        public static void Initialize()
+        private static int _counter;
+
+        public static void Initialize(GraphicsDebugLevel logLevel)
         {
+            // Disable everything
+            GL.DebugMessageControl(DebugSourceControl.DontCare, DebugTypeControl.DontCare, DebugSeverityControl.DontCare, 0, (int[])null, false);
+
+            if (logLevel == GraphicsDebugLevel.None)
+            {
+                GL.Disable(EnableCap.DebugOutputSynchronous);
+                GL.DebugMessageCallback(null, IntPtr.Zero);
+
+                return;
+            }
+
             GL.Enable(EnableCap.DebugOutputSynchronous);
 
-            GL.DebugMessageControl(DebugSourceControl.DontCare, DebugTypeControl.DontCare, DebugSeverityControl.DontCare, 0, (int[])null, true);
+            if (logLevel == GraphicsDebugLevel.Error)
+            {
+                GL.DebugMessageControl(DebugSourceControl.DontCare, DebugTypeControl.DebugTypeError, DebugSeverityControl.DontCare, 0, (int[])null, true);
+            }
+            else if (logLevel == GraphicsDebugLevel.Performance)
+            {
+                GL.DebugMessageControl(DebugSourceControl.DontCare, DebugTypeControl.DebugTypeError, DebugSeverityControl.DontCare, 0, (int[])null, true);
+                GL.DebugMessageControl(DebugSourceControl.DontCare, DebugTypeControl.DebugTypePerformance, DebugSeverityControl.DontCare, 0, (int[])null, true);
+            }
+            else
+            {
+                GL.DebugMessageControl(DebugSourceControl.DontCare, DebugTypeControl.DontCare, DebugSeverityControl.DontCare, 0, (int[])null, true);
+            }
 
+            _counter = 0;
             _debugCallback = GLDebugHandler;
 
             GL.DebugMessageCallback(_debugCallback, IntPtr.Zero);
+
+            Logger.PrintWarning(LogClass.Gpu, "OpenGL Debugging is enabled. Performance will be negatively impacted.");
         }
 
         private static void GLDebugHandler(
@@ -29,20 +59,43 @@ namespace Ryujinx.Graphics.OpenGL
             IntPtr message,
             IntPtr userParam)
         {
-            string fullMessage = $"{type} {severity} {source} {Marshal.PtrToStringAnsi(message)}";
+            string msg = Marshal.PtrToStringUTF8(message).Replace('\n', ' ');
 
             switch (type)
             {
-                case DebugType.DebugTypeError:
-                    Logger.PrintError(LogClass.Gpu, fullMessage);
-                    break;
-                case DebugType.DebugTypePerformance:
-                    Logger.PrintWarning(LogClass.Gpu, fullMessage);
-                    break;
+                case DebugType.DebugTypeError      : Logger.PrintError(LogClass.Gpu, $"{severity}: {msg}\nCallStack={Environment.StackTrace}", "GLERROR"); break;
+                case DebugType.DebugTypePerformance: Logger.PrintWarning(LogClass.Gpu, $"{severity}: {msg}", "GLPERF"); break;
+                case DebugType.DebugTypePushGroup  : Logger.PrintInfo(LogClass.Gpu, $"{{ ({id}) {severity}: {msg}", "GLINFO"); break;
+                case DebugType.DebugTypePopGroup   : Logger.PrintInfo(LogClass.Gpu, $"}} ({id}) {severity}: {msg}", "GLINFO"); break;
                 default:
-                    Logger.PrintDebug(LogClass.Gpu, fullMessage);
+                    if (source == DebugSource.DebugSourceApplication)
+                    {
+                        Logger.PrintInfo(LogClass.Gpu, $"{type} {severity}: {msg}", "GLINFO");
+                    }
+                    else
+                    {
+                        Logger.PrintDebug(LogClass.Gpu, $"{type} {severity}: {msg}", "GLDEBUG");
+                    }
                     break;
             }
         }
+
+        // Useful debug helpers
+        public static void PushGroup(string dbgMsg)
+        {
+            int counter = Interlocked.Increment(ref _counter);
+
+            GL.PushDebugGroup(DebugSourceExternal.DebugSourceApplication, counter, dbgMsg.Length, dbgMsg);
+        }
+
+        public static void PopGroup()
+        {
+            GL.PopDebugGroup();
+        }
+
+        public static void Print(string dbgMsg, DebugType type = DebugType.DebugTypeMarker, DebugSeverity severity = DebugSeverity.DebugSeverityNotification, int id = 999999)
+        {
+            GL.DebugMessageInsert(DebugSourceExternal.DebugSourceApplication, type, id, severity, dbgMsg.Length, dbgMsg);
+        }
     }
 }
diff --git a/Ryujinx.Graphics.OpenGL/Renderer.cs b/Ryujinx.Graphics.OpenGL/Renderer.cs
index 49324637bc..e7a8a96b79 100644
--- a/Ryujinx.Graphics.OpenGL/Renderer.cs
+++ b/Ryujinx.Graphics.OpenGL/Renderer.cs
@@ -1,4 +1,5 @@
 using OpenTK.Graphics.OpenGL;
+using Ryujinx.Common.Configuration;
 using Ryujinx.Common.Logging;
 using Ryujinx.Graphics.GAL;
 using Ryujinx.Graphics.OpenGL.Image;
@@ -96,8 +97,10 @@ namespace Ryujinx.Graphics.OpenGL
             return _counters.QueueReport(type, resultHandler);
         }
 
-        public void Initialize()
+        public void Initialize(GraphicsDebugLevel glLogLevel)
         {
+            Debugger.Initialize(glLogLevel);
+
             PrintGpuInformation();
 
             _counters.Initialize();
diff --git a/Ryujinx/Ui/GLRenderer.cs b/Ryujinx/Ui/GLRenderer.cs
index a8ed9156ce..867401ada1 100644
--- a/Ryujinx/Ui/GLRenderer.cs
+++ b/Ryujinx/Ui/GLRenderer.cs
@@ -5,6 +5,7 @@ using OpenTK.Graphics;
 using OpenTK.Graphics.OpenGL;
 using OpenTK.Input;
 using Ryujinx.Configuration;
+using Ryujinx.Common.Configuration;
 using Ryujinx.Common.Configuration.Hid;
 using Ryujinx.Graphics.OpenGL;
 using Ryujinx.HLE;
@@ -47,10 +48,14 @@ namespace Ryujinx.Ui
 
         private HotkeyButtons _prevHotkeyButtons;
 
-        public GlRenderer(Switch device)
+        private GraphicsDebugLevel _glLogLevel;
+
+        public GlRenderer(Switch device, GraphicsDebugLevel glLogLevel)
             : base (GetGraphicsMode(),
             3, 3,
-            GraphicsContextFlags.ForwardCompatible)
+            glLogLevel == GraphicsDebugLevel.None 
+            ? GraphicsContextFlags.ForwardCompatible 
+            : GraphicsContextFlags.ForwardCompatible | GraphicsContextFlags.Debug)
         {
             WaitEvent = new ManualResetEvent(false);
 
@@ -73,6 +78,8 @@ namespace Ryujinx.Ui
                           | EventMask.KeyReleaseMask));
 
             this.Shown += Renderer_Shown;
+
+            _glLogLevel = glLogLevel;
         }
 
         private static GraphicsMode GetGraphicsMode()
@@ -304,7 +311,7 @@ namespace Ryujinx.Ui
             // First take exclusivity on the OpenGL context.
             GraphicsContext.MakeCurrent(WindowInfo);
 
-            _renderer.Initialize();
+            _renderer.Initialize(_glLogLevel);
 
             // Make sure the first frame is not transparent.
             GL.ClearColor(OpenTK.Color.Black);
diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs
index 92e0c95678..4287010784 100644
--- a/Ryujinx/Ui/MainWindow.cs
+++ b/Ryujinx/Ui/MainWindow.cs
@@ -501,7 +501,7 @@ namespace Ryujinx.Ui
                 }
             ).ToArray());
 
-            _glWidget = new GlRenderer(_emulationContext);
+            _glWidget = new GlRenderer(_emulationContext, ConfigurationState.Instance.Logger.GraphicsDebugLevel);
 
             Application.Invoke(delegate
             {
diff --git a/Ryujinx/Ui/SettingsWindow.cs b/Ryujinx/Ui/SettingsWindow.cs
index b488fdbbb8..5fe51854ee 100644
--- a/Ryujinx/Ui/SettingsWindow.cs
+++ b/Ryujinx/Ui/SettingsWindow.cs
@@ -1,6 +1,7 @@
 using Gtk;
 using Ryujinx.Audio;
 using Ryujinx.Configuration;
+using Ryujinx.Common.Configuration;
 using Ryujinx.Common.Configuration.Hid;
 using Ryujinx.Configuration.System;
 using Ryujinx.HLE.HOS.Services.Time.TimeZone;
@@ -35,6 +36,7 @@ namespace Ryujinx.Ui
         [GUI] CheckButton     _guestLogToggle;
         [GUI] CheckButton     _fsAccessLogToggle;
         [GUI] Adjustment      _fsLogSpinAdjustment;
+        [GUI] ComboBoxText    _graphicsDebugLevel;
         [GUI] CheckButton     _dockedModeToggle;
         [GUI] CheckButton     _discordToggle;
         [GUI] CheckButton     _vSyncToggle;
@@ -149,6 +151,13 @@ namespace Ryujinx.Ui
                 _fsAccessLogToggle.Click();
             }
 
+            foreach (GraphicsDebugLevel level in Enum.GetValues(typeof(GraphicsDebugLevel)))
+            {
+                _graphicsDebugLevel.Append(level.ToString(), level.ToString());
+            }
+
+            _graphicsDebugLevel.SetActiveId(ConfigurationState.Instance.Logger.GraphicsDebugLevel.Value.ToString());
+
             if (ConfigurationState.Instance.System.EnableDockedMode)
             {
                 _dockedModeToggle.Click();
@@ -496,6 +505,7 @@ namespace Ryujinx.Ui
             ConfigurationState.Instance.Logger.EnableGuest.Value               = _guestLogToggle.Active;
             ConfigurationState.Instance.Logger.EnableFsAccessLog.Value         = _fsAccessLogToggle.Active;
             ConfigurationState.Instance.Logger.EnableFileLog.Value             = _fileLogToggle.Active;
+            ConfigurationState.Instance.Logger.GraphicsDebugLevel.Value        = Enum.Parse<GraphicsDebugLevel>(_graphicsDebugLevel.ActiveId);
             ConfigurationState.Instance.System.EnableDockedMode.Value          = _dockedModeToggle.Active;
             ConfigurationState.Instance.EnableDiscordIntegration.Value         = _discordToggle.Active;
             ConfigurationState.Instance.Graphics.EnableVsync.Value             = _vSyncToggle.Active;
diff --git a/Ryujinx/Ui/SettingsWindow.glade b/Ryujinx/Ui/SettingsWindow.glade
index 384fed4dcf..33ea05b75d 100644
--- a/Ryujinx/Ui/SettingsWindow.glade
+++ b/Ryujinx/Ui/SettingsWindow.glade
@@ -2007,24 +2007,6 @@
                                     <property name="position">1</property>
                                   </packing>
                                 </child>
-                                <child>
-                                  <object class="GtkCheckButton" id="_debugLogToggle">
-                                    <property name="label" translatable="yes">Enable Debug Logs</property>
-                                    <property name="visible">True</property>
-                                    <property name="can_focus">True</property>
-                                    <property name="receives_default">False</property>
-                                    <property name="tooltip_text" translatable="yes">Enables printing debug log messages</property>
-                                    <property name="halign">start</property>
-                                    <property name="margin_top">5</property>
-                                    <property name="margin_bottom">5</property>
-                                    <property name="draw_indicator">True</property>
-                                  </object>
-                                  <packing>
-                                    <property name="expand">False</property>
-                                    <property name="fill">True</property>
-                                    <property name="position">2</property>
-                                  </packing>
-                                </child>
                                 <child>
                                   <object class="GtkCheckButton" id="_stubLogToggle">
                                     <property name="label" translatable="yes">Enable Stub Logs</property>
@@ -2188,6 +2170,102 @@
                             <property name="position">0</property>
                           </packing>
                         </child>
+                        <child>
+                          <object class="GtkBox" id="CatDevLogging">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="margin_left">5</property>
+                            <property name="margin_right">5</property>
+                            <property name="margin_top">10</property>
+                            <property name="orientation">vertical</property>
+                            <child>
+                              <object class="GtkLabel">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="halign">start</property>
+                                <property name="margin_bottom">5</property>
+                                <property name="tooltip_text" translatable="yes">Use with care</property>
+                                <property name="label" translatable="yes">Developer Options</property>
+                                <attributes>
+                                  <attribute name="weight" value="bold"/>
+                                </attributes>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">20</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkBox" id="DevLoggingOptions">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="valign">start</property>
+                                <property name="margin_left">10</property>
+                                <property name="margin_right">10</property>
+                                <property name="orientation">vertical</property>
+                                <child>
+                                  <object class="GtkCheckButton" id="_debugLogToggle">
+                                    <property name="label" translatable="yes">Enable Debug Logs</property>
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="receives_default">False</property>
+                                    <property name="tooltip_text" translatable="yes">Enables printing debug log messages</property>
+                                    <property name="halign">start</property>
+                                    <property name="margin_top">5</property>
+                                    <property name="margin_bottom">5</property>
+                                    <property name="draw_indicator">True</property>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">21</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkBox">
+                                  <property name="visible">True</property>
+                                  <property name="can_focus">False</property>
+                                  <property name="margin_top">5</property>
+                                  <child>
+                                    <object class="GtkLabel">
+                                      <property name="visible">True</property>
+                                      <property name="can_focus">False</property>
+                                      <property name="tooltip_text" translatable="yes">Requires appropriate log levels enabled. Not persistent across restarts.</property>
+                                      <property name="label" translatable="yes">OpenGL Log Level</property>
+                                    </object>
+                                    <packing>
+                                      <property name="expand">False</property>
+                                      <property name="fill">True</property>
+                                      <property name="padding">5</property>
+                                      <property name="position">22</property>
+                                    </packing>
+                                  </child>
+                                  <child>
+                                    <object class="GtkComboBoxText" id="_graphicsDebugLevel">
+                                      <property name="visible">True</property>
+                                      <property name="can_focus">False</property>
+                                      <property name="tooltip_text" translatable="yes">Requires appropriate log levels enabled. Not persistent across restarts.</property>
+                                      <property name="margin_left">5</property>
+                                    </object>
+                                    <packing>
+                                      <property name="expand">False</property>
+                                      <property name="fill">True</property>
+                                      <property name="position">22</property>
+                                    </packing>
+                                  </child>
+                                  </object>
+                                </child>
+                              </object>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="padding">5</property>
+                            <property name="position">22</property>
+                          </packing>
+                        </child>
                       </object>
                       <packing>
                         <property name="position">4</property>