diff options
Diffstat (limited to 'Ryujinx.Ava/Ui/Backend/Vulkan/VulkanPhysicalDevice.cs')
-rw-r--r-- | Ryujinx.Ava/Ui/Backend/Vulkan/VulkanPhysicalDevice.cs | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanPhysicalDevice.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanPhysicalDevice.cs new file mode 100644 index 00000000..11444d30 --- /dev/null +++ b/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanPhysicalDevice.cs @@ -0,0 +1,219 @@ +using Ryujinx.Graphics.Vulkan; +using Silk.NET.Core; +using Silk.NET.Vulkan; +using Silk.NET.Vulkan.Extensions.KHR; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; + +namespace Ryujinx.Ava.Ui.Vulkan +{ + public unsafe class VulkanPhysicalDevice + { + private VulkanPhysicalDevice(PhysicalDevice apiHandle, Vk api, uint queueCount, uint queueFamilyIndex) + { + InternalHandle = apiHandle; + Api = api; + QueueCount = queueCount; + QueueFamilyIndex = queueFamilyIndex; + + api.GetPhysicalDeviceProperties(apiHandle, out var properties); + + DeviceName = Marshal.PtrToStringAnsi((IntPtr)properties.DeviceName); + DeviceId = VulkanInitialization.StringFromIdPair(properties.VendorID, properties.DeviceID); + + var version = (Version32)properties.ApiVersion; + ApiVersion = new Version((int)version.Major, (int)version.Minor, 0, (int)version.Patch); + } + + internal PhysicalDevice InternalHandle { get; } + internal Vk Api { get; } + public uint QueueCount { get; } + public uint QueueFamilyIndex { get; } + public IntPtr Handle => InternalHandle.Handle; + + public string DeviceName { get; } + public string DeviceId { get; } + public Version ApiVersion { get; } + public static Dictionary<PhysicalDevice, PhysicalDeviceProperties> PhysicalDevices { get; private set; } + public static IEnumerable<KeyValuePair<PhysicalDevice, PhysicalDeviceProperties>> SuitableDevices { get; private set; } + + internal static void SelectAvailableDevices(VulkanInstance instance, + VulkanSurface surface, bool preferDiscreteGpu, string preferredDevice) + { + uint physicalDeviceCount; + + instance.Api.EnumeratePhysicalDevices(instance.InternalHandle, &physicalDeviceCount, null).ThrowOnError(); + + var physicalDevices = new PhysicalDevice[physicalDeviceCount]; + + fixed (PhysicalDevice* pPhysicalDevices = physicalDevices) + { + instance.Api.EnumeratePhysicalDevices(instance.InternalHandle, &physicalDeviceCount, pPhysicalDevices) + .ThrowOnError(); + } + + PhysicalDevices = new Dictionary<PhysicalDevice, PhysicalDeviceProperties>(); + + foreach (var physicalDevice in physicalDevices) + { + instance.Api.GetPhysicalDeviceProperties(physicalDevice, out var properties); + PhysicalDevices.Add(physicalDevice, properties); + } + + SuitableDevices = PhysicalDevices.Where(x => IsSuitableDevice( + instance.Api, + x.Key, + x.Value, + surface.ApiHandle, + out _, + out _)); + } + + internal static VulkanPhysicalDevice FindSuitablePhysicalDevice(VulkanInstance instance, + VulkanSurface surface, bool preferDiscreteGpu, string preferredDevice) + { + SelectAvailableDevices(instance, surface, preferDiscreteGpu, preferredDevice); + + uint queueFamilyIndex = 0; + uint queueCount = 0; + + if (!string.IsNullOrWhiteSpace(preferredDevice)) + { + var physicalDevice = SuitableDevices.FirstOrDefault(x => VulkanInitialization.StringFromIdPair(x.Value.VendorID, x.Value.DeviceID) == preferredDevice); + + queueFamilyIndex = FindSuitableQueueFamily(instance.Api, physicalDevice.Key, + surface.ApiHandle, out queueCount); + if (queueFamilyIndex != int.MaxValue) + { + return new VulkanPhysicalDevice(physicalDevice.Key, instance.Api, queueCount, queueFamilyIndex); + } + } + + if (preferDiscreteGpu) + { + var discreteGpus = SuitableDevices.Where(p => p.Value.DeviceType == PhysicalDeviceType.DiscreteGpu); + + foreach (var gpu in discreteGpus) + { + queueFamilyIndex = FindSuitableQueueFamily(instance.Api, gpu.Key, + surface.ApiHandle, out queueCount); + if (queueFamilyIndex != int.MaxValue) + { + return new VulkanPhysicalDevice(gpu.Key, instance.Api, queueCount, queueFamilyIndex); + } + } + } + + foreach (var physicalDevice in SuitableDevices) + { + queueFamilyIndex = FindSuitableQueueFamily(instance.Api, physicalDevice.Key, + surface.ApiHandle, out queueCount); + if (queueFamilyIndex != int.MaxValue) + { + return new VulkanPhysicalDevice(physicalDevice.Key, instance.Api, queueCount, queueFamilyIndex); + } + } + + throw new Exception("No suitable physical device found"); + } + + private static unsafe bool IsSuitableDevice(Vk api, PhysicalDevice physicalDevice, PhysicalDeviceProperties properties, SurfaceKHR surface, + out uint queueCount, out uint familyIndex) + { + queueCount = 0; + familyIndex = 0; + + if (properties.DeviceType == PhysicalDeviceType.Cpu) return false; + + var extensionMatches = 0; + uint propertiesCount; + + api.EnumerateDeviceExtensionProperties(physicalDevice, (byte*)null, &propertiesCount, null).ThrowOnError(); + + var extensionProperties = new ExtensionProperties[propertiesCount]; + + fixed (ExtensionProperties* pExtensionProperties = extensionProperties) + { + api.EnumerateDeviceExtensionProperties( + physicalDevice, + (byte*)null, + &propertiesCount, + pExtensionProperties).ThrowOnError(); + + for (var i = 0; i < propertiesCount; i++) + { + var extensionName = Marshal.PtrToStringAnsi((IntPtr)pExtensionProperties[i].ExtensionName); + + if (VulkanInitialization.RequiredExtensions.Contains(extensionName)) + { + extensionMatches++; + } + } + } + + if (extensionMatches == VulkanInitialization.RequiredExtensions.Length) + { + familyIndex = FindSuitableQueueFamily(api, physicalDevice, surface, out queueCount); + + return familyIndex != uint.MaxValue; + } + + return false; + } + + internal unsafe string[] GetSupportedExtensions() + { + uint propertiesCount; + + Api.EnumerateDeviceExtensionProperties(InternalHandle, (byte*)null, &propertiesCount, null).ThrowOnError(); + + var extensionProperties = new ExtensionProperties[propertiesCount]; + + fixed (ExtensionProperties* pExtensionProperties = extensionProperties) + { + Api.EnumerateDeviceExtensionProperties(InternalHandle, (byte*)null, &propertiesCount, pExtensionProperties) + .ThrowOnError(); + } + + return extensionProperties.Select(x => Marshal.PtrToStringAnsi((IntPtr)x.ExtensionName)).ToArray(); + } + + private static uint FindSuitableQueueFamily(Vk api, PhysicalDevice physicalDevice, SurfaceKHR surface, + out uint queueCount) + { + const QueueFlags RequiredFlags = QueueFlags.QueueGraphicsBit | QueueFlags.QueueComputeBit; + + var khrSurface = new KhrSurface(api.Context); + + uint propertiesCount; + + api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, &propertiesCount, null); + + var properties = new QueueFamilyProperties[propertiesCount]; + + fixed (QueueFamilyProperties* pProperties = properties) + { + api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, &propertiesCount, pProperties); + } + + for (uint index = 0; index < propertiesCount; index++) + { + var queueFlags = properties[index].QueueFlags; + + khrSurface.GetPhysicalDeviceSurfaceSupport(physicalDevice, index, surface, out var surfaceSupported) + .ThrowOnError(); + + if (queueFlags.HasFlag(RequiredFlags) && surfaceSupported) + { + queueCount = properties[index].QueueCount; + return index; + } + } + + queueCount = 0; + return uint.MaxValue; + } + } +} |