ryujinx/Ryujinx.Graphics.Vulkan/VulkanDebugMessenger.cs
Mary f5a6f45b27
vulkan: Separate debug utils logic from VulkanInitialization (#4609)
* vulkan: Separate debug utils logic from VulkanInitialization

Also checks for VK_EXT_debug_utils existence instead of force enabling it and allow possible error during messenger init

* Address gdkchan's comment

* Use CreateDebugUtilsMessenger Span variant
2023-04-01 08:05:02 +00:00

154 lines
6.1 KiB
C#

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 static string[] _excludedMessages = new string[]
{
// NOTE: Done on purpose right now.
"UNASSIGNED-CoreValidation-Shader-OutputNotConsumed",
// TODO: Figure out if fixable
"VUID-vkCmdDrawIndexed-None-04584",
// TODO: Might be worth looking into making this happy to possibly optimize copies.
"UNASSIGNED-CoreValidation-DrawState-InvalidImageLayout",
// TODO: Fix this, it's causing too much noise right now.
"VUID-VkSubpassDependency-srcSubpass-00867"
};
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;
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,
_ => throw new ArgumentException($"Invalid log level \"{_logLevel}\".")
};
var messageSeverity = _logLevel switch
{
GraphicsDebugLevel.Error => DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt,
GraphicsDebugLevel.Slowdowns => DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt |
DebugUtilsMessageSeverityFlagsEXT.WarningBitExt,
GraphicsDebugLevel.All => DebugUtilsMessageSeverityFlagsEXT.InfoBitExt |
DebugUtilsMessageSeverityFlagsEXT.WarningBitExt |
DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt |
DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt,
_ => throw new ArgumentException($"Invalid log level \"{_logLevel}\".")
};
var debugUtilsMessengerCreateInfo = new DebugUtilsMessengerCreateInfoEXT()
{
SType = StructureType.DebugUtilsMessengerCreateInfoExt,
MessageType = messageType,
MessageSeverity = messageSeverity
};
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);
foreach (string excludedMessagePart in _excludedMessages)
{
if (msg.Contains(excludedMessagePart))
{
return 0;
}
}
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;
}
}
}
}