2023-04-01 10:05:02 +02:00
|
|
|
using Ryujinx.Common.Configuration;
|
|
|
|
using Ryujinx.Common.Logging;
|
|
|
|
using Ryujinx.Common.Utilities;
|
|
|
|
using Silk.NET.Vulkan;
|
|
|
|
using Silk.NET.Vulkan.Extensions.EXT;
|
|
|
|
using System;
|
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
|
|
|
|
namespace Ryujinx.Graphics.Vulkan
|
|
|
|
{
|
|
|
|
class VulkanDebugMessenger : IDisposable
|
|
|
|
{
|
|
|
|
private readonly Vk _api;
|
|
|
|
private readonly Instance _instance;
|
|
|
|
private readonly GraphicsDebugLevel _logLevel;
|
|
|
|
private readonly ExtDebugUtils _debugUtils;
|
|
|
|
private readonly DebugUtilsMessengerEXT? _debugUtilsMessenger;
|
|
|
|
private bool _disposed;
|
|
|
|
|
|
|
|
public VulkanDebugMessenger(Vk api, Instance instance, GraphicsDebugLevel logLevel)
|
|
|
|
{
|
|
|
|
_api = api;
|
|
|
|
_instance = instance;
|
|
|
|
_logLevel = logLevel;
|
|
|
|
|
|
|
|
_api.TryGetInstanceExtension(instance, out _debugUtils);
|
|
|
|
|
|
|
|
Result result = TryInitialize(out _debugUtilsMessenger);
|
|
|
|
|
|
|
|
if (result != Result.Success)
|
|
|
|
{
|
|
|
|
Logger.Error?.Print(LogClass.Gpu, $"Vulkan debug messenger initialization failed with error {result}");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private Result TryInitialize(out DebugUtilsMessengerEXT? debugUtilsMessengerHandle)
|
|
|
|
{
|
|
|
|
debugUtilsMessengerHandle = null;
|
2023-07-01 12:31:42 +02:00
|
|
|
|
2023-04-01 10:05:02 +02:00
|
|
|
if (_debugUtils != null && _logLevel != GraphicsDebugLevel.None)
|
|
|
|
{
|
|
|
|
var messageType = _logLevel switch
|
|
|
|
{
|
|
|
|
GraphicsDebugLevel.Error => DebugUtilsMessageTypeFlagsEXT.ValidationBitExt,
|
|
|
|
GraphicsDebugLevel.Slowdowns => DebugUtilsMessageTypeFlagsEXT.ValidationBitExt |
|
|
|
|
DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt,
|
|
|
|
GraphicsDebugLevel.All => DebugUtilsMessageTypeFlagsEXT.GeneralBitExt |
|
|
|
|
DebugUtilsMessageTypeFlagsEXT.ValidationBitExt |
|
|
|
|
DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt,
|
2023-07-01 12:31:42 +02:00
|
|
|
_ => throw new ArgumentException($"Invalid log level \"{_logLevel}\"."),
|
2023-04-01 10:05:02 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
var messageSeverity = _logLevel switch
|
|
|
|
{
|
|
|
|
GraphicsDebugLevel.Error => DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt,
|
|
|
|
GraphicsDebugLevel.Slowdowns => DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt |
|
|
|
|
DebugUtilsMessageSeverityFlagsEXT.WarningBitExt,
|
|
|
|
GraphicsDebugLevel.All => DebugUtilsMessageSeverityFlagsEXT.InfoBitExt |
|
|
|
|
DebugUtilsMessageSeverityFlagsEXT.WarningBitExt |
|
|
|
|
DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt |
|
|
|
|
DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt,
|
2023-07-01 12:31:42 +02:00
|
|
|
_ => throw new ArgumentException($"Invalid log level \"{_logLevel}\"."),
|
2023-04-01 10:05:02 +02:00
|
|
|
};
|
|
|
|
|
2023-07-01 12:31:42 +02:00
|
|
|
var debugUtilsMessengerCreateInfo = new DebugUtilsMessengerCreateInfoEXT
|
2023-04-01 10:05:02 +02:00
|
|
|
{
|
|
|
|
SType = StructureType.DebugUtilsMessengerCreateInfoExt,
|
|
|
|
MessageType = messageType,
|
2023-07-01 12:31:42 +02:00
|
|
|
MessageSeverity = messageSeverity,
|
2023-04-01 10:05:02 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
unsafe
|
|
|
|
{
|
|
|
|
debugUtilsMessengerCreateInfo.PfnUserCallback = new PfnDebugUtilsMessengerCallbackEXT(UserCallback);
|
|
|
|
}
|
|
|
|
|
|
|
|
DebugUtilsMessengerEXT messengerHandle = default;
|
|
|
|
|
|
|
|
Result result = _debugUtils.CreateDebugUtilsMessenger(_instance, SpanHelpers.AsReadOnlySpan(ref debugUtilsMessengerCreateInfo), ReadOnlySpan<AllocationCallbacks>.Empty, SpanHelpers.AsSpan(ref messengerHandle));
|
|
|
|
|
|
|
|
if (result == Result.Success)
|
|
|
|
{
|
|
|
|
debugUtilsMessengerHandle = messengerHandle;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Result.Success;
|
|
|
|
}
|
|
|
|
|
|
|
|
private unsafe static uint UserCallback(
|
|
|
|
DebugUtilsMessageSeverityFlagsEXT messageSeverity,
|
|
|
|
DebugUtilsMessageTypeFlagsEXT messageTypes,
|
|
|
|
DebugUtilsMessengerCallbackDataEXT* pCallbackData,
|
|
|
|
void* pUserData)
|
|
|
|
{
|
|
|
|
var msg = Marshal.PtrToStringAnsi((IntPtr)pCallbackData->PMessage);
|
|
|
|
|
|
|
|
if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt))
|
|
|
|
{
|
|
|
|
Logger.Error?.Print(LogClass.Gpu, msg);
|
|
|
|
}
|
|
|
|
else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.WarningBitExt))
|
|
|
|
{
|
|
|
|
Logger.Warning?.Print(LogClass.Gpu, msg);
|
|
|
|
}
|
|
|
|
else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.InfoBitExt))
|
|
|
|
{
|
|
|
|
Logger.Info?.Print(LogClass.Gpu, msg);
|
|
|
|
}
|
|
|
|
else // if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt))
|
|
|
|
{
|
|
|
|
Logger.Debug?.Print(LogClass.Gpu, msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
{
|
|
|
|
if (!_disposed)
|
|
|
|
{
|
|
|
|
if (_debugUtilsMessenger.HasValue)
|
|
|
|
{
|
|
|
|
_debugUtils.DestroyDebugUtilsMessenger(_instance, _debugUtilsMessenger.Value, Span<AllocationCallbacks>.Empty);
|
|
|
|
}
|
|
|
|
|
|
|
|
_disposed = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|