diff --git a/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs b/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs
index 6a786a96c8..3139e20918 100644
--- a/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs
+++ b/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs
@@ -9,21 +9,18 @@ namespace Ryujinx.Graphics.Vulkan
         private ulong MaxDeviceMemoryUsageEstimate = 16UL * 1024 * 1024 * 1024;
 
         private readonly Vk _api;
-        private readonly PhysicalDevice _physicalDevice;
+        private readonly VulkanPhysicalDevice _physicalDevice;
         private readonly Device _device;
         private readonly List<MemoryAllocatorBlockList> _blockLists;
         private readonly int _blockAlignment;
-        private readonly PhysicalDeviceMemoryProperties _physicalDeviceMemoryProperties;
 
-        public MemoryAllocator(Vk api, PhysicalDevice physicalDevice, Device device, uint maxMemoryAllocationCount)
+        public MemoryAllocator(Vk api, VulkanPhysicalDevice physicalDevice, Device device)
         {
             _api = api;
             _physicalDevice = physicalDevice;
             _device = device;
             _blockLists = new List<MemoryAllocatorBlockList>();
-            _blockAlignment = (int)Math.Min(int.MaxValue, MaxDeviceMemoryUsageEstimate / (ulong)maxMemoryAllocationCount);
-
-            _api.GetPhysicalDeviceMemoryProperties(_physicalDevice, out _physicalDeviceMemoryProperties);
+            _blockAlignment = (int)Math.Min(int.MaxValue, MaxDeviceMemoryUsageEstimate / (ulong)_physicalDevice.PhysicalDeviceProperties.Limits.MaxMemoryAllocationCount);
         }
 
         public MemoryAllocation AllocateDeviceMemory(
@@ -64,9 +61,9 @@ namespace Ryujinx.Graphics.Vulkan
             uint memoryTypeBits,
             MemoryPropertyFlags flags)
         {
-            for (int i = 0; i < _physicalDeviceMemoryProperties.MemoryTypeCount; i++)
+            for (int i = 0; i < _physicalDevice.PhysicalDeviceMemoryProperties.MemoryTypeCount; i++)
             {
-                var type = _physicalDeviceMemoryProperties.MemoryTypes[i];
+                var type = _physicalDevice.PhysicalDeviceMemoryProperties.MemoryTypes[i];
 
                 if ((memoryTypeBits & (1 << i)) != 0)
                 {
@@ -80,15 +77,11 @@ namespace Ryujinx.Graphics.Vulkan
             return -1;
         }
 
-        public static bool IsDeviceMemoryShared(Vk api, PhysicalDevice physicalDevice)
+        public static bool IsDeviceMemoryShared(VulkanPhysicalDevice physicalDevice)
         {
-            // The device is regarded as having shared memory if all heaps have the device local bit.
-
-            api.GetPhysicalDeviceMemoryProperties(physicalDevice, out var properties);
-
-            for (int i = 0; i < properties.MemoryHeapCount; i++)
+            for (int i = 0; i < physicalDevice.PhysicalDeviceMemoryProperties.MemoryHeapCount; i++)
             {
-                if (!properties.MemoryHeaps[i].Flags.HasFlag(MemoryHeapFlags.DeviceLocalBit))
+                if (!physicalDevice.PhysicalDeviceMemoryProperties.MemoryHeaps[i].Flags.HasFlag(MemoryHeapFlags.DeviceLocalBit))
                 {
                     return false;
                 }
diff --git a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs
index f04ab5c051..4f69cb1d24 100644
--- a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs
+++ b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs
@@ -47,35 +47,23 @@ namespace Ryujinx.Graphics.Vulkan
             KhrSwapchain.ExtensionName
         };
 
-        internal static Instance CreateInstance(Vk api, GraphicsDebugLevel logLevel, string[] requiredExtensions)
+        internal static VulkanInstance CreateInstance(Vk api, GraphicsDebugLevel logLevel, string[] requiredExtensions)
         {
             var enabledLayers = new List<string>();
 
+            var instanceExtensions = VulkanInstance.GetInstanceExtensions(api);
+            var instanceLayers = VulkanInstance.GetInstanceLayers(api);
+
             void AddAvailableLayer(string layerName)
             {
-                uint layerPropertiesCount;
-
-                api.EnumerateInstanceLayerProperties(&layerPropertiesCount, null).ThrowOnError();
-
-                LayerProperties[] layerProperties = new LayerProperties[layerPropertiesCount];
-
-                fixed (LayerProperties* pLayerProperties = layerProperties)
+                if (instanceLayers.Contains(layerName))
                 {
-                    api.EnumerateInstanceLayerProperties(&layerPropertiesCount, layerProperties).ThrowOnError();
-
-                    for (int i = 0; i < layerPropertiesCount; i++)
-                    {
-                        string currentLayerName = Marshal.PtrToStringAnsi((IntPtr)pLayerProperties[i].LayerName);
-
-                        if (currentLayerName == layerName)
-                        {
-                            enabledLayers.Add(layerName);
-                            return;
-                        }
-                    }
+                    enabledLayers.Add(layerName);
+                }
+                else
+                {
+                    Logger.Warning?.Print(LogClass.Gpu, $"Missing layer {layerName}");
                 }
-
-                Logger.Warning?.Print(LogClass.Gpu, $"Missing layer {layerName}");
             }
 
             if (logLevel != GraphicsDebugLevel.None)
@@ -85,7 +73,7 @@ namespace Ryujinx.Graphics.Vulkan
 
             var enabledExtensions = requiredExtensions;
 
-            if (api.IsInstanceExtensionPresent("VK_EXT_debug_utils"))
+            if (instanceExtensions.Contains("VK_EXT_debug_utils"))
             {
                 enabledExtensions = enabledExtensions.Append(ExtDebugUtils.ExtensionName).ToArray();
             }
@@ -124,7 +112,7 @@ namespace Ryujinx.Graphics.Vulkan
                 EnabledLayerCount = (uint)enabledLayers.Count
             };
 
-            api.CreateInstance(in instanceCreateInfo, null, out var instance).ThrowOnError();
+            Result result = VulkanInstance.Create(api, ref instanceCreateInfo, out var instance);
 
             Marshal.FreeHGlobal(appName);
 
@@ -138,21 +126,14 @@ namespace Ryujinx.Graphics.Vulkan
                 Marshal.FreeHGlobal(ppEnabledLayers[i]);
             }
 
+            result.ThrowOnError();
+
             return instance;
         }
 
-        internal static PhysicalDevice FindSuitablePhysicalDevice(Vk api, Instance instance, SurfaceKHR surface, string preferredGpuId)
+        internal static VulkanPhysicalDevice FindSuitablePhysicalDevice(Vk api, VulkanInstance instance, SurfaceKHR surface, string preferredGpuId)
         {
-            uint physicalDeviceCount;
-
-            api.EnumeratePhysicalDevices(instance, &physicalDeviceCount, null).ThrowOnError();
-
-            PhysicalDevice[] physicalDevices = new PhysicalDevice[physicalDeviceCount];
-
-            fixed (PhysicalDevice* pPhysicalDevices = physicalDevices)
-            {
-                api.EnumeratePhysicalDevices(instance, &physicalDeviceCount, pPhysicalDevices).ThrowOnError();
-            }
+            instance.EnumeratePhysicalDevices(out var physicalDevices).ThrowOnError();
 
             // First we try to pick the the user preferred GPU.
             for (int i = 0; i < physicalDevices.Length; i++)
@@ -198,76 +179,41 @@ namespace Ryujinx.Graphics.Vulkan
                 EnabledLayerCount = 0
             };
 
-            api.CreateInstance(in instanceCreateInfo, null, out var instance).ThrowOnError();
-
-            // We ensure that vkEnumerateInstanceVersion is present (added in 1.1).
-            // If the instance doesn't support it, no device is going to be 1.1 compatible.
-            if (api.GetInstanceProcAddr(instance, "vkEnumerateInstanceVersion") == IntPtr.Zero)
-            {
-                api.DestroyInstance(instance, null);
-
-                return Array.Empty<DeviceInfo>();
-            }
-
-            // We currently assume that the instance is compatible with Vulkan 1.2
-            // TODO: Remove this once we relax our initialization codepaths.
-            uint instanceApiVerison = 0;
-            api.EnumerateInstanceVersion(ref instanceApiVerison).ThrowOnError();
-
-            if (instanceApiVerison < MinimalInstanceVulkanVersion)
-            {
-                api.DestroyInstance(instance, null);
-
-                return Array.Empty<DeviceInfo>();
-            }
+            Result result = VulkanInstance.Create(api, ref instanceCreateInfo, out var rawInstance);
 
             Marshal.FreeHGlobal(appName);
 
-            uint physicalDeviceCount;
+            result.ThrowOnError();
 
-            api.EnumeratePhysicalDevices(instance, &physicalDeviceCount, null).ThrowOnError();
+            using VulkanInstance instance = rawInstance;
 
-            PhysicalDevice[] physicalDevices = new PhysicalDevice[physicalDeviceCount];
-
-            fixed (PhysicalDevice* pPhysicalDevices = physicalDevices)
+            // We currently assume that the instance is compatible with Vulkan 1.2
+            // TODO: Remove this once we relax our initialization codepaths.
+            if (instance.InstanceVersion < MinimalInstanceVulkanVersion)
             {
-                api.EnumeratePhysicalDevices(instance, &physicalDeviceCount, pPhysicalDevices).ThrowOnError();
+                return Array.Empty<DeviceInfo>();
             }
 
-            DeviceInfo[] devices = new DeviceInfo[physicalDevices.Length];
+            instance.EnumeratePhysicalDevices(out VulkanPhysicalDevice[] physicalDevices).ThrowOnError();
 
-            for (int i = 0; i < physicalDevices.Length; i++)
+            List<DeviceInfo> deviceInfos = new List<DeviceInfo>();
+
+            foreach (VulkanPhysicalDevice physicalDevice in physicalDevices)
             {
-                var physicalDevice = physicalDevices[i];
-                api.GetPhysicalDeviceProperties(physicalDevice, out var properties);
-
-                if (properties.ApiVersion < MinimalVulkanVersion)
+                if (physicalDevice.PhysicalDeviceProperties.ApiVersion < MinimalVulkanVersion)
                 {
                     continue;
                 }
 
-                devices[i] = new DeviceInfo(
-                    StringFromIdPair(properties.VendorID, properties.DeviceID),
-                    VendorUtils.GetNameFromId(properties.VendorID),
-                    Marshal.PtrToStringAnsi((IntPtr)properties.DeviceName),
-                    properties.DeviceType == PhysicalDeviceType.DiscreteGpu);
+                deviceInfos.Add(physicalDevice.ToDeviceInfo());
             }
 
-            api.DestroyInstance(instance, null);
-
-            return devices;
+            return deviceInfos.ToArray();
         }
 
-        public static string StringFromIdPair(uint vendorId, uint deviceId)
+        private static bool IsPreferredAndSuitableDevice(Vk api, VulkanPhysicalDevice physicalDevice, SurfaceKHR surface, string preferredGpuId)
         {
-            return $"0x{vendorId:X}_0x{deviceId:X}";
-        }
-
-        private static bool IsPreferredAndSuitableDevice(Vk api, PhysicalDevice physicalDevice, SurfaceKHR surface, string preferredGpuId)
-        {
-            api.GetPhysicalDeviceProperties(physicalDevice, out var properties);
-
-            if (StringFromIdPair(properties.VendorID, properties.DeviceID) != preferredGpuId)
+            if (physicalDevice.Id != preferredGpuId)
             {
                 return false;
             }
@@ -275,68 +221,47 @@ namespace Ryujinx.Graphics.Vulkan
             return IsSuitableDevice(api, physicalDevice, surface);
         }
 
-        private static bool IsSuitableDevice(Vk api, PhysicalDevice physicalDevice, SurfaceKHR surface)
+        private static bool IsSuitableDevice(Vk api, VulkanPhysicalDevice physicalDevice, SurfaceKHR surface)
         {
             int extensionMatches = 0;
-            uint propertiesCount;
 
-            api.EnumerateDeviceExtensionProperties(physicalDevice, (byte*)null, &propertiesCount, null).ThrowOnError();
-
-            ExtensionProperties[] extensionProperties = new ExtensionProperties[propertiesCount];
-
-            fixed (ExtensionProperties* pExtensionProperties = extensionProperties)
+            foreach (string requiredExtension in _requiredExtensions)
             {
-                api.EnumerateDeviceExtensionProperties(physicalDevice, (byte*)null, &propertiesCount, pExtensionProperties).ThrowOnError();
-
-                for (int i = 0; i < propertiesCount; i++)
+                if (physicalDevice.IsDeviceExtensionPresent(requiredExtension))
                 {
-                    string extensionName = Marshal.PtrToStringAnsi((IntPtr)pExtensionProperties[i].ExtensionName);
-
-                    if (_requiredExtensions.Contains(extensionName))
-                    {
-                        extensionMatches++;
-                    }
+                    extensionMatches++;
                 }
             }
 
             return extensionMatches == _requiredExtensions.Length && FindSuitableQueueFamily(api, physicalDevice, surface, out _) != InvalidIndex;
         }
 
-        internal static uint FindSuitableQueueFamily(Vk api, PhysicalDevice physicalDevice, SurfaceKHR surface, out uint queueCount)
+        internal static uint FindSuitableQueueFamily(Vk api, VulkanPhysicalDevice physicalDevice, SurfaceKHR surface, out uint queueCount)
         {
             const QueueFlags RequiredFlags = QueueFlags.GraphicsBit | QueueFlags.ComputeBit;
 
             var khrSurface = new KhrSurface(api.Context);
 
-            uint propertiesCount;
-
-            api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, &propertiesCount, null);
-
-            QueueFamilyProperties[] properties = new QueueFamilyProperties[propertiesCount];
-
-            fixed (QueueFamilyProperties* pProperties = properties)
+            for (uint index = 0; index < physicalDevice.QueueFamilyProperties.Length; index++)
             {
-                api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, &propertiesCount, pProperties);
-            }
+                ref QueueFamilyProperties property = ref physicalDevice.QueueFamilyProperties[index];
 
-            for (uint index = 0; index < propertiesCount; index++)
-            {
-                var queueFlags = properties[index].QueueFlags;
+                khrSurface.GetPhysicalDeviceSurfaceSupport(physicalDevice.PhysicalDevice, index, surface, out var surfaceSupported).ThrowOnError();
 
-                khrSurface.GetPhysicalDeviceSurfaceSupport(physicalDevice, index, surface, out var surfaceSupported).ThrowOnError();
-
-                if (queueFlags.HasFlag(RequiredFlags) && surfaceSupported)
+                if (property.QueueFlags.HasFlag(RequiredFlags) && surfaceSupported)
                 {
-                    queueCount = properties[index].QueueCount;
+                    queueCount = property.QueueCount;
+
                     return index;
                 }
             }
 
             queueCount = 0;
+
             return InvalidIndex;
         }
 
-        public static Device CreateDevice(Vk api, PhysicalDevice physicalDevice, uint queueFamilyIndex, string[] supportedExtensions, uint queueCount)
+        internal static Device CreateDevice(Vk api, VulkanPhysicalDevice physicalDevice, uint queueFamilyIndex, uint queueCount)
         {
             if (queueCount > QueuesCount)
             {
@@ -358,8 +283,7 @@ namespace Ryujinx.Graphics.Vulkan
                 PQueuePriorities = queuePriorities
             };
 
-            api.GetPhysicalDeviceProperties(physicalDevice, out var properties);
-            bool useRobustBufferAccess = VendorUtils.FromId(properties.VendorID) == Vendor.Nvidia;
+            bool useRobustBufferAccess = VendorUtils.FromId(physicalDevice.PhysicalDeviceProperties.VendorID) == Vendor.Nvidia;
 
             PhysicalDeviceFeatures2 features2 = new PhysicalDeviceFeatures2()
             {
@@ -380,7 +304,7 @@ namespace Ryujinx.Graphics.Vulkan
                 PNext = features2.PNext
             };
 
-            if (supportedExtensions.Contains("VK_EXT_custom_border_color"))
+            if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_custom_border_color"))
             {
                 features2.PNext = &supportedFeaturesCustomBorderColor;
             }
@@ -391,7 +315,7 @@ namespace Ryujinx.Graphics.Vulkan
                 PNext = features2.PNext
             };
 
-            if (supportedExtensions.Contains("VK_EXT_primitive_topology_list_restart"))
+            if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_primitive_topology_list_restart"))
             {
                 features2.PNext = &supportedFeaturesPrimitiveTopologyListRestart;
             }
@@ -402,7 +326,7 @@ namespace Ryujinx.Graphics.Vulkan
                 PNext = features2.PNext
             };
 
-            if (supportedExtensions.Contains(ExtTransformFeedback.ExtensionName))
+            if (physicalDevice.IsDeviceExtensionPresent(ExtTransformFeedback.ExtensionName))
             {
                 features2.PNext = &supportedFeaturesTransformFeedback;
             }
@@ -412,14 +336,14 @@ namespace Ryujinx.Graphics.Vulkan
                 SType = StructureType.PhysicalDeviceRobustness2FeaturesExt
             };
 
-            if (supportedExtensions.Contains("VK_EXT_robustness2"))
+            if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_robustness2"))
             {
                 supportedFeaturesRobustness2.PNext = features2.PNext;
 
                 features2.PNext = &supportedFeaturesRobustness2;
             }
 
-            api.GetPhysicalDeviceFeatures2(physicalDevice, &features2);
+            api.GetPhysicalDeviceFeatures2(physicalDevice.PhysicalDevice, &features2);
 
             var supportedFeatures = features2.Features;
 
@@ -452,7 +376,7 @@ namespace Ryujinx.Graphics.Vulkan
 
             PhysicalDeviceTransformFeedbackFeaturesEXT featuresTransformFeedback;
 
-            if (supportedExtensions.Contains(ExtTransformFeedback.ExtensionName))
+            if (physicalDevice.IsDeviceExtensionPresent(ExtTransformFeedback.ExtensionName))
             {
                 featuresTransformFeedback = new PhysicalDeviceTransformFeedbackFeaturesEXT()
                 {
@@ -466,7 +390,7 @@ namespace Ryujinx.Graphics.Vulkan
 
             PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT featuresPrimitiveTopologyListRestart;
 
-            if (supportedExtensions.Contains("VK_EXT_primitive_topology_list_restart"))
+            if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_primitive_topology_list_restart"))
             {
                 featuresPrimitiveTopologyListRestart = new PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT()
                 {
@@ -481,7 +405,7 @@ namespace Ryujinx.Graphics.Vulkan
 
             PhysicalDeviceRobustness2FeaturesEXT featuresRobustness2;
 
-            if (supportedExtensions.Contains("VK_EXT_robustness2"))
+            if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_robustness2"))
             {
                 featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT()
                 {
@@ -497,7 +421,7 @@ namespace Ryujinx.Graphics.Vulkan
             {
                 SType = StructureType.PhysicalDeviceExtendedDynamicStateFeaturesExt,
                 PNext = pExtendedFeatures,
-                ExtendedDynamicState = supportedExtensions.Contains(ExtExtendedDynamicState.ExtensionName)
+                ExtendedDynamicState = physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName)
             };
 
             pExtendedFeatures = &featuresExtendedDynamicState;
@@ -515,16 +439,16 @@ namespace Ryujinx.Graphics.Vulkan
             {
                 SType = StructureType.PhysicalDeviceVulkan12Features,
                 PNext = pExtendedFeatures,
-                DescriptorIndexing = supportedExtensions.Contains("VK_EXT_descriptor_indexing"),
-                DrawIndirectCount = supportedExtensions.Contains(KhrDrawIndirectCount.ExtensionName),
-                UniformBufferStandardLayout = supportedExtensions.Contains("VK_KHR_uniform_buffer_standard_layout")
+                DescriptorIndexing = physicalDevice.IsDeviceExtensionPresent("VK_EXT_descriptor_indexing"),
+                DrawIndirectCount = physicalDevice.IsDeviceExtensionPresent(KhrDrawIndirectCount.ExtensionName),
+                UniformBufferStandardLayout = physicalDevice.IsDeviceExtensionPresent("VK_KHR_uniform_buffer_standard_layout")
             };
 
             pExtendedFeatures = &featuresVk12;
 
             PhysicalDeviceIndexTypeUint8FeaturesEXT featuresIndexU8;
 
-            if (supportedExtensions.Contains("VK_EXT_index_type_uint8"))
+            if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_index_type_uint8"))
             {
                 featuresIndexU8 = new PhysicalDeviceIndexTypeUint8FeaturesEXT()
                 {
@@ -538,7 +462,7 @@ namespace Ryujinx.Graphics.Vulkan
 
             PhysicalDeviceFragmentShaderInterlockFeaturesEXT featuresFragmentShaderInterlock;
 
-            if (supportedExtensions.Contains("VK_EXT_fragment_shader_interlock"))
+            if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_fragment_shader_interlock"))
             {
                 featuresFragmentShaderInterlock = new PhysicalDeviceFragmentShaderInterlockFeaturesEXT()
                 {
@@ -552,7 +476,7 @@ namespace Ryujinx.Graphics.Vulkan
 
             PhysicalDeviceSubgroupSizeControlFeaturesEXT featuresSubgroupSizeControl;
 
-            if (supportedExtensions.Contains("VK_EXT_subgroup_size_control"))
+            if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_subgroup_size_control"))
             {
                 featuresSubgroupSizeControl = new PhysicalDeviceSubgroupSizeControlFeaturesEXT()
                 {
@@ -566,7 +490,7 @@ namespace Ryujinx.Graphics.Vulkan
 
             PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColor;
 
-            if (supportedExtensions.Contains("VK_EXT_custom_border_color") &&
+            if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_custom_border_color") &&
                 supportedFeaturesCustomBorderColor.CustomBorderColors &&
                 supportedFeaturesCustomBorderColor.CustomBorderColorWithoutFormat)
             {
@@ -581,7 +505,7 @@ namespace Ryujinx.Graphics.Vulkan
                 pExtendedFeatures = &featuresCustomBorderColor;
             }
 
-            var enabledExtensions = _requiredExtensions.Union(_desirableExtensions.Intersect(supportedExtensions)).ToArray();
+            var enabledExtensions = _requiredExtensions.Union(_desirableExtensions.Intersect(physicalDevice.DeviceExtensions)).ToArray();
 
             IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length];
 
@@ -601,7 +525,7 @@ namespace Ryujinx.Graphics.Vulkan
                 PEnabledFeatures = &features
             };
 
-            api.CreateDevice(physicalDevice, in deviceCreateInfo, null, out var device).ThrowOnError();
+            api.CreateDevice(physicalDevice.PhysicalDevice, in deviceCreateInfo, null, out var device).ThrowOnError();
 
             for (int i = 0; i < enabledExtensions.Length; i++)
             {
@@ -610,21 +534,5 @@ namespace Ryujinx.Graphics.Vulkan
 
             return device;
         }
-
-        public static string[] GetSupportedExtensions(Vk api, PhysicalDevice physicalDevice)
-        {
-            uint propertiesCount;
-
-            api.EnumerateDeviceExtensionProperties(physicalDevice, (byte*)null, &propertiesCount, null).ThrowOnError();
-
-            ExtensionProperties[] extensionProperties = new ExtensionProperties[propertiesCount];
-
-            fixed (ExtensionProperties* pExtensionProperties = extensionProperties)
-            {
-                api.EnumerateDeviceExtensionProperties(physicalDevice, (byte*)null, &propertiesCount, pExtensionProperties).ThrowOnError();
-            }
-
-            return extensionProperties.Select(x => Marshal.PtrToStringAnsi((IntPtr)x.ExtensionName)).ToArray();
-        }
     }
 }
diff --git a/Ryujinx.Graphics.Vulkan/VulkanInstance.cs b/Ryujinx.Graphics.Vulkan/VulkanInstance.cs
new file mode 100644
index 0000000000..843d341250
--- /dev/null
+++ b/Ryujinx.Graphics.Vulkan/VulkanInstance.cs
@@ -0,0 +1,127 @@
+using Ryujinx.Common.Utilities;
+using Silk.NET.Core;
+using Silk.NET.Vulkan;
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Graphics.Vulkan
+{
+    class VulkanInstance : IDisposable
+    {
+        private readonly Vk _api;
+        public readonly Instance Instance;
+        public readonly Version32 InstanceVersion;
+
+        private bool _disposed;
+
+        private VulkanInstance(Vk api, Instance instance)
+        {
+            _api = api;
+            Instance = instance;
+
+            if (api.GetInstanceProcAddr(instance, "vkEnumerateInstanceVersion") == IntPtr.Zero)
+            {
+                InstanceVersion = Vk.Version10;
+            }
+            else
+            {
+                uint rawInstanceVersion = 0;
+
+                if (api.EnumerateInstanceVersion(ref rawInstanceVersion) != Result.Success)
+                {
+                    rawInstanceVersion = Vk.Version11.Value;
+                }
+
+                InstanceVersion = (Version32)rawInstanceVersion;
+            }
+        }
+
+        public static Result Create(Vk api, ref InstanceCreateInfo createInfo, out VulkanInstance instance)
+        {
+            instance = null;
+
+            Instance rawInstance = default;
+
+            Result result = api.CreateInstance(SpanHelpers.AsReadOnlySpan(ref createInfo), ReadOnlySpan<AllocationCallbacks>.Empty, SpanHelpers.AsSpan(ref rawInstance));
+
+            if (result == Result.Success)
+            {
+                instance = new VulkanInstance(api, rawInstance);
+            }
+
+            return result;
+        }
+
+        public Result EnumeratePhysicalDevices(out VulkanPhysicalDevice[] physicalDevices)
+        {
+            physicalDevices = null;
+
+            uint physicalDeviceCount = 0;
+
+            Result result = _api.EnumeratePhysicalDevices(Instance, SpanHelpers.AsSpan(ref physicalDeviceCount), Span<PhysicalDevice>.Empty);
+
+            if (result != Result.Success)
+            {
+                return result;
+            }
+
+            PhysicalDevice[] rawPhysicalDevices = new PhysicalDevice[physicalDeviceCount];
+
+            result = _api.EnumeratePhysicalDevices(Instance, SpanHelpers.AsSpan(ref physicalDeviceCount), rawPhysicalDevices);
+
+            if (result != Result.Success)
+            {
+                return result;
+            }
+
+            physicalDevices = rawPhysicalDevices.Select(x => new VulkanPhysicalDevice(_api, x)).ToArray();
+
+            return Result.Success;
+        }
+
+        public static IReadOnlySet<string> GetInstanceExtensions(Vk api)
+        {
+            uint propertiesCount = 0;
+
+            api.EnumerateInstanceExtensionProperties(ReadOnlySpan<byte>.Empty, SpanHelpers.AsSpan(ref propertiesCount), Span<ExtensionProperties>.Empty).ThrowOnError();
+
+            ExtensionProperties[] extensionProperties = new ExtensionProperties[propertiesCount];
+
+            api.EnumerateInstanceExtensionProperties(ReadOnlySpan<byte>.Empty, SpanHelpers.AsSpan(ref propertiesCount), extensionProperties).ThrowOnError();
+
+            unsafe
+            {
+                return extensionProperties.Select(x => Marshal.PtrToStringAnsi((IntPtr)x.ExtensionName)).ToImmutableHashSet();
+            }
+        }
+
+        public static IReadOnlySet<string> GetInstanceLayers(Vk api)
+        {
+            uint propertiesCount = 0;
+
+            api.EnumerateInstanceLayerProperties(SpanHelpers.AsSpan(ref propertiesCount), Span<LayerProperties>.Empty).ThrowOnError();
+
+            LayerProperties[] layerProperties = new LayerProperties[propertiesCount];
+
+            api.EnumerateInstanceLayerProperties(SpanHelpers.AsSpan(ref propertiesCount), layerProperties).ThrowOnError();
+
+            unsafe
+            {
+                return layerProperties.Select(x => Marshal.PtrToStringAnsi((IntPtr)x.LayerName)).ToImmutableHashSet();
+            }
+        }
+
+        public void Dispose()
+        {
+            if (!_disposed)
+            {
+                _api.DestroyInstance(Instance, ReadOnlySpan<AllocationCallbacks>.Empty);
+
+                _disposed = true;
+            }
+        }
+    }
+}
diff --git a/Ryujinx.Graphics.Vulkan/VulkanPhysicalDevice.cs b/Ryujinx.Graphics.Vulkan/VulkanPhysicalDevice.cs
new file mode 100644
index 0000000000..547f36543f
--- /dev/null
+++ b/Ryujinx.Graphics.Vulkan/VulkanPhysicalDevice.cs
@@ -0,0 +1,70 @@
+using Ryujinx.Common.Utilities;
+using Ryujinx.Graphics.GAL;
+using Silk.NET.Vulkan;
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Graphics.Vulkan
+{
+    readonly struct VulkanPhysicalDevice
+    {
+        public readonly PhysicalDevice PhysicalDevice;
+        public readonly PhysicalDeviceFeatures PhysicalDeviceFeatures;
+        public readonly PhysicalDeviceProperties PhysicalDeviceProperties;
+        public readonly PhysicalDeviceMemoryProperties PhysicalDeviceMemoryProperties;
+        public readonly QueueFamilyProperties[] QueueFamilyProperties;
+        public readonly string DeviceName;
+        public readonly IReadOnlySet<string> DeviceExtensions;
+
+        public VulkanPhysicalDevice(Vk api, PhysicalDevice physicalDevice)
+        {
+            PhysicalDevice = physicalDevice;
+            PhysicalDeviceFeatures = api.GetPhysicalDeviceFeature(PhysicalDevice);
+
+            api.GetPhysicalDeviceProperties(PhysicalDevice, out var physicalDeviceProperties);
+            PhysicalDeviceProperties = physicalDeviceProperties;
+
+            api.GetPhysicalDeviceMemoryProperties(PhysicalDevice, out PhysicalDeviceMemoryProperties);
+
+            unsafe
+            {
+                DeviceName = Marshal.PtrToStringAnsi((IntPtr)physicalDeviceProperties.DeviceName);
+            }
+
+            uint propertiesCount = 0;
+
+            api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, SpanHelpers.AsSpan(ref propertiesCount), Span<QueueFamilyProperties>.Empty);
+
+            QueueFamilyProperties = new QueueFamilyProperties[propertiesCount];
+
+            api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, SpanHelpers.AsSpan(ref propertiesCount), QueueFamilyProperties);
+
+            api.EnumerateDeviceExtensionProperties(PhysicalDevice, Span<byte>.Empty, SpanHelpers.AsSpan(ref propertiesCount), Span<ExtensionProperties>.Empty).ThrowOnError();
+
+            ExtensionProperties[] extensionProperties = new ExtensionProperties[propertiesCount];
+
+            api.EnumerateDeviceExtensionProperties(PhysicalDevice, Span<byte>.Empty, SpanHelpers.AsSpan(ref propertiesCount), extensionProperties).ThrowOnError();
+
+            unsafe
+            {
+                DeviceExtensions = extensionProperties.Select(x => Marshal.PtrToStringAnsi((IntPtr)x.ExtensionName)).ToImmutableHashSet();
+            }
+        }
+
+        public string Id => $"0x{PhysicalDeviceProperties.VendorID:X}_0x{PhysicalDeviceProperties.DeviceID:X}";
+
+        public bool IsDeviceExtensionPresent(string extension) => DeviceExtensions.Contains(extension);
+
+        public DeviceInfo ToDeviceInfo()
+        {
+            return new DeviceInfo(
+                Id,
+                VendorUtils.GetNameFromId(PhysicalDeviceProperties.VendorID),
+                DeviceName,
+                PhysicalDeviceProperties.DeviceType == PhysicalDeviceType.DiscreteGpu);
+        }
+    }
+}
diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
index 81dec12ec8..193cdce314 100644
--- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
+++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
@@ -17,9 +17,9 @@ namespace Ryujinx.Graphics.Vulkan
 {
     public sealed class VulkanRenderer : IRenderer
     {
-        private Instance _instance;
+        private VulkanInstance _instance;
         private SurfaceKHR _surface;
-        private PhysicalDevice _physicalDevice;
+        private VulkanPhysicalDevice _physicalDevice;
         private Device _device;
         private WindowBase _window;
 
@@ -106,33 +106,31 @@ namespace Ryujinx.Graphics.Vulkan
             }
         }
 
-        private unsafe void LoadFeatures(string[] supportedExtensions, uint maxQueueCount, uint queueFamilyIndex)
+        private unsafe void LoadFeatures(uint maxQueueCount, uint queueFamilyIndex)
         {
-            FormatCapabilities = new FormatCapabilities(Api, _physicalDevice);
+            FormatCapabilities = new FormatCapabilities(Api, _physicalDevice.PhysicalDevice);
 
-            var supportedFeatures = Api.GetPhysicalDeviceFeature(_physicalDevice);
-
-            if (Api.TryGetDeviceExtension(_instance, _device, out ExtConditionalRendering conditionalRenderingApi))
+            if (Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtConditionalRendering conditionalRenderingApi))
             {
                 ConditionalRenderingApi = conditionalRenderingApi;
             }
 
-            if (Api.TryGetDeviceExtension(_instance, _device, out ExtExtendedDynamicState extendedDynamicStateApi))
+            if (Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtExtendedDynamicState extendedDynamicStateApi))
             {
                 ExtendedDynamicStateApi = extendedDynamicStateApi;
             }
 
-            if (Api.TryGetDeviceExtension(_instance, _device, out KhrPushDescriptor pushDescriptorApi))
+            if (Api.TryGetDeviceExtension(_instance.Instance, _device, out KhrPushDescriptor pushDescriptorApi))
             {
                 PushDescriptorApi = pushDescriptorApi;
             }
 
-            if (Api.TryGetDeviceExtension(_instance, _device, out ExtTransformFeedback transformFeedbackApi))
+            if (Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtTransformFeedback transformFeedbackApi))
             {
                 TransformFeedbackApi = transformFeedbackApi;
             }
 
-            if (Api.TryGetDeviceExtension(_instance, _device, out KhrDrawIndirectCount drawIndirectCountApi))
+            if (Api.TryGetDeviceExtension(_instance.Instance, _device, out KhrDrawIndirectCount drawIndirectCountApi))
             {
                 DrawIndirectCountApi = drawIndirectCountApi;
             }
@@ -154,7 +152,7 @@ namespace Ryujinx.Graphics.Vulkan
                 SType = StructureType.PhysicalDeviceBlendOperationAdvancedPropertiesExt
             };
 
-            bool supportsBlendOperationAdvanced = supportedExtensions.Contains("VK_EXT_blend_operation_advanced");
+            bool supportsBlendOperationAdvanced = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_blend_operation_advanced");
 
             if (supportsBlendOperationAdvanced)
             {
@@ -167,14 +165,14 @@ namespace Ryujinx.Graphics.Vulkan
                 SType = StructureType.PhysicalDeviceSubgroupSizeControlPropertiesExt
             };
 
-            bool supportsSubgroupSizeControl = supportedExtensions.Contains("VK_EXT_subgroup_size_control");
+            bool supportsSubgroupSizeControl = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_subgroup_size_control");
 
             if (supportsSubgroupSizeControl)
             {
                 properties2.PNext = &propertiesSubgroupSizeControl;
             }
 
-            bool supportsTransformFeedback = supportedExtensions.Contains(ExtTransformFeedback.ExtensionName);
+            bool supportsTransformFeedback = _physicalDevice.IsDeviceExtensionPresent(ExtTransformFeedback.ExtensionName);
 
             PhysicalDeviceTransformFeedbackPropertiesEXT propertiesTransformFeedback = new PhysicalDeviceTransformFeedbackPropertiesEXT()
             {
@@ -222,30 +220,30 @@ namespace Ryujinx.Graphics.Vulkan
                 SType = StructureType.PhysicalDevicePortabilitySubsetFeaturesKhr
             };
 
-            if (supportedExtensions.Contains("VK_EXT_primitive_topology_list_restart"))
+            if (_physicalDevice.IsDeviceExtensionPresent("VK_EXT_primitive_topology_list_restart"))
             {
                 features2.PNext = &featuresPrimitiveTopologyListRestart;
             }
 
-            if (supportedExtensions.Contains("VK_EXT_robustness2"))
+            if (_physicalDevice.IsDeviceExtensionPresent("VK_EXT_robustness2"))
             {
                 featuresRobustness2.PNext = features2.PNext;
                 features2.PNext = &featuresRobustness2;
             }
 
-            if (supportedExtensions.Contains("VK_KHR_shader_float16_int8"))
+            if (_physicalDevice.IsDeviceExtensionPresent("VK_KHR_shader_float16_int8"))
             {
                 featuresShaderInt8.PNext = features2.PNext;
                 features2.PNext = &featuresShaderInt8;
             }
 
-            if (supportedExtensions.Contains("VK_EXT_custom_border_color"))
+            if (_physicalDevice.IsDeviceExtensionPresent("VK_EXT_custom_border_color"))
             {
                 featuresCustomBorderColor.PNext = features2.PNext;
                 features2.PNext = &featuresCustomBorderColor;
             }
 
-            bool usePortability = supportedExtensions.Contains("VK_KHR_portability_subset");
+            bool usePortability = _physicalDevice.IsDeviceExtensionPresent("VK_KHR_portability_subset");
 
             if (usePortability)
             {
@@ -256,8 +254,8 @@ namespace Ryujinx.Graphics.Vulkan
                 features2.PNext = &featuresPortabilitySubset;
             }
 
-            Api.GetPhysicalDeviceProperties2(_physicalDevice, &properties2);
-            Api.GetPhysicalDeviceFeatures2(_physicalDevice, &features2);
+            Api.GetPhysicalDeviceProperties2(_physicalDevice.PhysicalDevice, &properties2);
+            Api.GetPhysicalDeviceFeatures2(_physicalDevice.PhysicalDevice, &features2);
 
             var portabilityFlags = PortabilitySubsetFlags.None;
             uint vertexBufferAlignment = 1;
@@ -272,7 +270,7 @@ namespace Ryujinx.Graphics.Vulkan
                 portabilityFlags |= featuresPortabilitySubset.SamplerMipLodBias ? 0 : PortabilitySubsetFlags.NoLodBias;
             }
 
-            bool supportsCustomBorderColor = supportedExtensions.Contains("VK_EXT_custom_border_color") &&
+            bool supportsCustomBorderColor = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_custom_border_color") &&
                                              featuresCustomBorderColor.CustomBorderColors &&
                                              featuresCustomBorderColor.CustomBorderColorWithoutFormat;
 
@@ -284,30 +282,30 @@ namespace Ryujinx.Graphics.Vulkan
                 properties.Limits.FramebufferStencilSampleCounts;
 
             Capabilities = new HardwareCapabilities(
-                supportedExtensions.Contains("VK_EXT_index_type_uint8"),
+                _physicalDevice.IsDeviceExtensionPresent("VK_EXT_index_type_uint8"),
                 supportsCustomBorderColor,
                 supportsBlendOperationAdvanced,
                 propertiesBlendOperationAdvanced.AdvancedBlendCorrelatedOverlap,
                 propertiesBlendOperationAdvanced.AdvancedBlendNonPremultipliedSrcColor,
                 propertiesBlendOperationAdvanced.AdvancedBlendNonPremultipliedDstColor,
-                supportedExtensions.Contains(KhrDrawIndirectCount.ExtensionName),
-                supportedExtensions.Contains("VK_EXT_fragment_shader_interlock"),
-                supportedExtensions.Contains("VK_NV_geometry_shader_passthrough"),
+                _physicalDevice.IsDeviceExtensionPresent(KhrDrawIndirectCount.ExtensionName),
+                _physicalDevice.IsDeviceExtensionPresent("VK_EXT_fragment_shader_interlock"),
+                _physicalDevice.IsDeviceExtensionPresent("VK_NV_geometry_shader_passthrough"),
                 supportsSubgroupSizeControl,
                 featuresShaderInt8.ShaderInt8,
-                supportedExtensions.Contains("VK_EXT_shader_stencil_export"),
-                supportedExtensions.Contains(ExtConditionalRendering.ExtensionName),
-                supportedExtensions.Contains(ExtExtendedDynamicState.ExtensionName),
+                _physicalDevice.IsDeviceExtensionPresent("VK_EXT_shader_stencil_export"),
+                _physicalDevice.IsDeviceExtensionPresent(ExtConditionalRendering.ExtensionName),
+                _physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName),
                 features2.Features.MultiViewport,
                 featuresRobustness2.NullDescriptor || IsMoltenVk,
-                supportedExtensions.Contains(KhrPushDescriptor.ExtensionName),
+                _physicalDevice.IsDeviceExtensionPresent(KhrPushDescriptor.ExtensionName),
                 featuresPrimitiveTopologyListRestart.PrimitiveTopologyListRestart,
                 featuresPrimitiveTopologyListRestart.PrimitiveTopologyPatchListRestart,
                 supportsTransformFeedback,
                 propertiesTransformFeedback.TransformFeedbackQueries,
                 features2.Features.OcclusionQueryPrecise,
-                supportedFeatures.PipelineStatisticsQuery,
-                supportedFeatures.GeometryShader,
+                _physicalDevice.PhysicalDeviceFeatures.PipelineStatisticsQuery,
+                _physicalDevice.PhysicalDeviceFeatures.GeometryShader,
                 propertiesSubgroupSizeControl.MinSubgroupSize,
                 propertiesSubgroupSizeControl.MaxSubgroupSize,
                 propertiesSubgroupSizeControl.RequiredSubgroupSizeStages,
@@ -315,9 +313,9 @@ namespace Ryujinx.Graphics.Vulkan
                 portabilityFlags,
                 vertexBufferAlignment);
 
-            IsSharedMemory = MemoryAllocator.IsDeviceMemoryShared(Api, _physicalDevice);
+            IsSharedMemory = MemoryAllocator.IsDeviceMemoryShared(_physicalDevice);
 
-            MemoryAllocator = new MemoryAllocator(Api, _physicalDevice, _device, properties.Limits.MaxMemoryAllocationCount);
+            MemoryAllocator = new MemoryAllocator(Api, _physicalDevice, _device);
 
             CommandBufferPool = new CommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex);
 
@@ -345,22 +343,21 @@ namespace Ryujinx.Graphics.Vulkan
             Api = api;
 
             _instance = VulkanInitialization.CreateInstance(api, logLevel, _getRequiredExtensions());
-            _debugMessenger = new VulkanDebugMessenger(api, _instance, logLevel);
+            _debugMessenger = new VulkanDebugMessenger(api, _instance.Instance, logLevel);
 
-            if (api.TryGetInstanceExtension(_instance, out KhrSurface surfaceApi))
+            if (api.TryGetInstanceExtension(_instance.Instance, out KhrSurface surfaceApi))
             {
                 SurfaceApi = surfaceApi;
             }
 
-            _surface = _getSurface(_instance, api);
+            _surface = _getSurface(_instance.Instance, api);
             _physicalDevice = VulkanInitialization.FindSuitablePhysicalDevice(api, _instance, _surface, _preferredGpuId);
 
             var queueFamilyIndex = VulkanInitialization.FindSuitableQueueFamily(api, _physicalDevice, _surface, out uint maxQueueCount);
-            var supportedExtensions = VulkanInitialization.GetSupportedExtensions(api, _physicalDevice);
 
-            _device = VulkanInitialization.CreateDevice(api, _physicalDevice, queueFamilyIndex, supportedExtensions, maxQueueCount);
+            _device = VulkanInitialization.CreateDevice(api, _physicalDevice, queueFamilyIndex, maxQueueCount);
 
-            if (api.TryGetDeviceExtension(_instance, _device, out KhrSwapchain swapchainApi))
+            if (api.TryGetDeviceExtension(_instance.Instance, _device, out KhrSwapchain swapchainApi))
             {
                 SwapchainApi = swapchainApi;
             }
@@ -369,9 +366,9 @@ namespace Ryujinx.Graphics.Vulkan
             Queue = queue;
             QueueLock = new object();
 
-            LoadFeatures(supportedExtensions, maxQueueCount, queueFamilyIndex);
+            LoadFeatures(maxQueueCount, queueFamilyIndex);
 
-            _window = new Window(this, _surface, _physicalDevice, _device);
+            _window = new Window(this, _surface, _physicalDevice.PhysicalDevice, _device);
 
             _initialized = true;
         }
@@ -536,10 +533,9 @@ namespace Ryujinx.Graphics.Vulkan
                 PNext = &featuresVk12
             };
 
-            Api.GetPhysicalDeviceFeatures2(_physicalDevice, &features2);
-            Api.GetPhysicalDeviceProperties(_physicalDevice, out var properties);
+            Api.GetPhysicalDeviceFeatures2(_physicalDevice.PhysicalDevice, &features2);
 
-            var limits = properties.Limits;
+            var limits = _physicalDevice.PhysicalDeviceProperties.Limits;
 
             return new Capabilities(
                 api: TargetApi.Vulkan,
@@ -623,7 +619,7 @@ namespace Ryujinx.Graphics.Vulkan
 
         private unsafe void PrintGpuInformation()
         {
-            Api.GetPhysicalDeviceProperties(_physicalDevice, out var properties);
+            var properties = _physicalDevice.PhysicalDeviceProperties;
 
             string vendorName = VendorUtils.GetNameFromId(properties.VendorID);
 
@@ -807,14 +803,14 @@ namespace Ryujinx.Graphics.Vulkan
                 sampler.Dispose();
             }
 
-            SurfaceApi.DestroySurface(_instance, _surface, null);
+            SurfaceApi.DestroySurface(_instance.Instance, _surface, null);
 
             Api.DestroyDevice(_device, null);
 
             _debugMessenger.Dispose();
 
             // Last step destroy the instance
-            Api.DestroyInstance(_instance, null);
+            _instance.Dispose();
         }
     }
 }
\ No newline at end of file