aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Graphics.Vulkan/VulkanDebugMessenger.cs
blob: 7e39a251ed468aea28dd89787f917a6482c9d1ed (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
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;
            }
        }
    }
}