From c95be55091eaee446e1ad51ad215b82be5866bb3 Mon Sep 17 00:00:00 2001 From: Mary Date: Wed, 5 Apr 2023 19:48:38 +0200 Subject: [PATCH] vulkan: Cleanup PhysicalDevice and Instance querying (#4632) * vulkan: Move most of the properties enumeration to VulkanPhysicalDevice That clean up a bit of duplicate logic. Also move to use an hashset for device extensions. * vulkan: Move instance querying to VulkanInstance Also cleanup code to use span when possible instead of unsafe pointers. * Address gdkchan's comments --- Ryujinx.Graphics.Vulkan/MemoryAllocator.cs | 23 +- .../VulkanInitialization.cs | 220 +++++------------- Ryujinx.Graphics.Vulkan/VulkanInstance.cs | 127 ++++++++++ .../VulkanPhysicalDevice.cs | 70 ++++++ Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 92 ++++---- 5 files changed, 313 insertions(+), 219 deletions(-) create mode 100644 Ryujinx.Graphics.Vulkan/VulkanInstance.cs create mode 100644 Ryujinx.Graphics.Vulkan/VulkanPhysicalDevice.cs diff --git a/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs b/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs index 6a786a96..3139e209 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 _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(); - _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 f04ab5c0..4f69cb1d 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(); + 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(); - } - - // 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(); - } + 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[] devices = new DeviceInfo[physicalDevices.Length]; + instance.EnumeratePhysicalDevices(out VulkanPhysicalDevice[] physicalDevices).ThrowOnError(); - for (int i = 0; i < physicalDevices.Length; i++) + List deviceInfos = new List(); + + 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 00000000..843d3412 --- /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.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.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 GetInstanceExtensions(Vk api) + { + uint propertiesCount = 0; + + api.EnumerateInstanceExtensionProperties(ReadOnlySpan.Empty, SpanHelpers.AsSpan(ref propertiesCount), Span.Empty).ThrowOnError(); + + ExtensionProperties[] extensionProperties = new ExtensionProperties[propertiesCount]; + + api.EnumerateInstanceExtensionProperties(ReadOnlySpan.Empty, SpanHelpers.AsSpan(ref propertiesCount), extensionProperties).ThrowOnError(); + + unsafe + { + return extensionProperties.Select(x => Marshal.PtrToStringAnsi((IntPtr)x.ExtensionName)).ToImmutableHashSet(); + } + } + + public static IReadOnlySet GetInstanceLayers(Vk api) + { + uint propertiesCount = 0; + + api.EnumerateInstanceLayerProperties(SpanHelpers.AsSpan(ref propertiesCount), Span.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.Empty); + + _disposed = true; + } + } + } +} diff --git a/Ryujinx.Graphics.Vulkan/VulkanPhysicalDevice.cs b/Ryujinx.Graphics.Vulkan/VulkanPhysicalDevice.cs new file mode 100644 index 00000000..547f3654 --- /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 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.Empty); + + QueueFamilyProperties = new QueueFamilyProperties[propertiesCount]; + + api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, SpanHelpers.AsSpan(ref propertiesCount), QueueFamilyProperties); + + api.EnumerateDeviceExtensionProperties(PhysicalDevice, Span.Empty, SpanHelpers.AsSpan(ref propertiesCount), Span.Empty).ThrowOnError(); + + ExtensionProperties[] extensionProperties = new ExtensionProperties[propertiesCount]; + + api.EnumerateDeviceExtensionProperties(PhysicalDevice, Span.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 81dec12e..193cdce3 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