/ src / Ryujinx.Graphics.Vulkan / VulkanDebugMessenger.cs
VulkanDebugMessenger.cs
  1  using Ryujinx.Common.Configuration;
  2  using Ryujinx.Common.Logging;
  3  using Ryujinx.Common.Utilities;
  4  using Silk.NET.Vulkan;
  5  using Silk.NET.Vulkan.Extensions.EXT;
  6  using System;
  7  using System.Runtime.InteropServices;
  8  
  9  namespace Ryujinx.Graphics.Vulkan
 10  {
 11      class VulkanDebugMessenger : IDisposable
 12      {
 13          private readonly Vk _api;
 14          private readonly Instance _instance;
 15          private readonly GraphicsDebugLevel _logLevel;
 16          private readonly ExtDebugUtils _debugUtils;
 17          private readonly DebugUtilsMessengerEXT? _debugUtilsMessenger;
 18          private bool _disposed;
 19  
 20          public VulkanDebugMessenger(Vk api, Instance instance, GraphicsDebugLevel logLevel)
 21          {
 22              _api = api;
 23              _instance = instance;
 24              _logLevel = logLevel;
 25  
 26              _api.TryGetInstanceExtension(instance, out _debugUtils);
 27  
 28              Result result = TryInitialize(out _debugUtilsMessenger);
 29  
 30              if (result != Result.Success)
 31              {
 32                  Logger.Error?.Print(LogClass.Gpu, $"Vulkan debug messenger initialization failed with error {result}");
 33              }
 34          }
 35  
 36          private Result TryInitialize(out DebugUtilsMessengerEXT? debugUtilsMessengerHandle)
 37          {
 38              debugUtilsMessengerHandle = null;
 39  
 40              if (_debugUtils != null && _logLevel != GraphicsDebugLevel.None)
 41              {
 42                  var messageType = _logLevel switch
 43                  {
 44                      GraphicsDebugLevel.Error => DebugUtilsMessageTypeFlagsEXT.ValidationBitExt,
 45                      GraphicsDebugLevel.Slowdowns => DebugUtilsMessageTypeFlagsEXT.ValidationBitExt |
 46                                                      DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt,
 47                      GraphicsDebugLevel.All => DebugUtilsMessageTypeFlagsEXT.GeneralBitExt |
 48                                                DebugUtilsMessageTypeFlagsEXT.ValidationBitExt |
 49                                                DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt,
 50                      _ => throw new ArgumentException($"Invalid log level \"{_logLevel}\"."),
 51                  };
 52  
 53                  var messageSeverity = _logLevel switch
 54                  {
 55                      GraphicsDebugLevel.Error => DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt,
 56                      GraphicsDebugLevel.Slowdowns => DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt |
 57                                                      DebugUtilsMessageSeverityFlagsEXT.WarningBitExt,
 58                      GraphicsDebugLevel.All => DebugUtilsMessageSeverityFlagsEXT.InfoBitExt |
 59                                                DebugUtilsMessageSeverityFlagsEXT.WarningBitExt |
 60                                                DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt |
 61                                                DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt,
 62                      _ => throw new ArgumentException($"Invalid log level \"{_logLevel}\"."),
 63                  };
 64  
 65                  var debugUtilsMessengerCreateInfo = new DebugUtilsMessengerCreateInfoEXT
 66                  {
 67                      SType = StructureType.DebugUtilsMessengerCreateInfoExt,
 68                      MessageType = messageType,
 69                      MessageSeverity = messageSeverity,
 70                  };
 71  
 72                  unsafe
 73                  {
 74                      debugUtilsMessengerCreateInfo.PfnUserCallback = new PfnDebugUtilsMessengerCallbackEXT(UserCallback);
 75                  }
 76  
 77                  DebugUtilsMessengerEXT messengerHandle = default;
 78  
 79                  Result result = _debugUtils.CreateDebugUtilsMessenger(_instance, SpanHelpers.AsReadOnlySpan(ref debugUtilsMessengerCreateInfo), ReadOnlySpan<AllocationCallbacks>.Empty, SpanHelpers.AsSpan(ref messengerHandle));
 80  
 81                  if (result == Result.Success)
 82                  {
 83                      debugUtilsMessengerHandle = messengerHandle;
 84                  }
 85  
 86                  return result;
 87              }
 88  
 89              return Result.Success;
 90          }
 91  
 92          private unsafe static uint UserCallback(
 93              DebugUtilsMessageSeverityFlagsEXT messageSeverity,
 94              DebugUtilsMessageTypeFlagsEXT messageTypes,
 95              DebugUtilsMessengerCallbackDataEXT* pCallbackData,
 96              void* pUserData)
 97          {
 98              var msg = Marshal.PtrToStringAnsi((IntPtr)pCallbackData->PMessage);
 99  
100              if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt))
101              {
102                  Logger.Error?.Print(LogClass.Gpu, msg);
103              }
104              else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.WarningBitExt))
105              {
106                  Logger.Warning?.Print(LogClass.Gpu, msg);
107              }
108              else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.InfoBitExt))
109              {
110                  Logger.Info?.Print(LogClass.Gpu, msg);
111              }
112              else // if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt))
113              {
114                  Logger.Debug?.Print(LogClass.Gpu, msg);
115              }
116  
117              return 0;
118          }
119  
120          public void Dispose()
121          {
122              if (!_disposed)
123              {
124                  if (_debugUtilsMessenger.HasValue)
125                  {
126                      _debugUtils.DestroyDebugUtilsMessenger(_instance, _debugUtilsMessenger.Value, Span<AllocationCallbacks>.Empty);
127                  }
128  
129                  _disposed = true;
130              }
131          }
132      }
133  }