VulkanPhysicalDevice.cs 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. using Ryujinx.Graphics.Vulkan;
  2. using Silk.NET.Core;
  3. using Silk.NET.Vulkan;
  4. using Silk.NET.Vulkan.Extensions.KHR;
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Linq;
  8. using System.Runtime.InteropServices;
  9. namespace Ryujinx.Ava.Ui.Vulkan
  10. {
  11. public unsafe class VulkanPhysicalDevice
  12. {
  13. private VulkanPhysicalDevice(PhysicalDevice apiHandle, Vk api, uint queueCount, uint queueFamilyIndex)
  14. {
  15. InternalHandle = apiHandle;
  16. Api = api;
  17. QueueCount = queueCount;
  18. QueueFamilyIndex = queueFamilyIndex;
  19. api.GetPhysicalDeviceProperties(apiHandle, out var properties);
  20. DeviceName = Marshal.PtrToStringAnsi((IntPtr)properties.DeviceName);
  21. DeviceId = VulkanInitialization.StringFromIdPair(properties.VendorID, properties.DeviceID);
  22. var version = (Version32)properties.ApiVersion;
  23. ApiVersion = new Version((int)version.Major, (int)version.Minor, 0, (int)version.Patch);
  24. }
  25. internal PhysicalDevice InternalHandle { get; }
  26. internal Vk Api { get; }
  27. public uint QueueCount { get; }
  28. public uint QueueFamilyIndex { get; }
  29. public IntPtr Handle => InternalHandle.Handle;
  30. public string DeviceName { get; }
  31. public string DeviceId { get; }
  32. public Version ApiVersion { get; }
  33. public static Dictionary<PhysicalDevice, PhysicalDeviceProperties> PhysicalDevices { get; private set; }
  34. public static IEnumerable<KeyValuePair<PhysicalDevice, PhysicalDeviceProperties>> SuitableDevices { get; private set; }
  35. internal static void SelectAvailableDevices(VulkanInstance instance,
  36. VulkanSurface surface, bool preferDiscreteGpu, string preferredDevice)
  37. {
  38. uint physicalDeviceCount;
  39. instance.Api.EnumeratePhysicalDevices(instance.InternalHandle, &physicalDeviceCount, null).ThrowOnError();
  40. var physicalDevices = new PhysicalDevice[physicalDeviceCount];
  41. fixed (PhysicalDevice* pPhysicalDevices = physicalDevices)
  42. {
  43. instance.Api.EnumeratePhysicalDevices(instance.InternalHandle, &physicalDeviceCount, pPhysicalDevices)
  44. .ThrowOnError();
  45. }
  46. PhysicalDevices = new Dictionary<PhysicalDevice, PhysicalDeviceProperties>();
  47. foreach (var physicalDevice in physicalDevices)
  48. {
  49. instance.Api.GetPhysicalDeviceProperties(physicalDevice, out var properties);
  50. PhysicalDevices.Add(physicalDevice, properties);
  51. }
  52. SuitableDevices = PhysicalDevices.Where(x => IsSuitableDevice(
  53. instance.Api,
  54. x.Key,
  55. x.Value,
  56. surface.ApiHandle,
  57. out _,
  58. out _));
  59. }
  60. internal static VulkanPhysicalDevice FindSuitablePhysicalDevice(VulkanInstance instance,
  61. VulkanSurface surface, bool preferDiscreteGpu, string preferredDevice)
  62. {
  63. SelectAvailableDevices(instance, surface, preferDiscreteGpu, preferredDevice);
  64. uint queueFamilyIndex = 0;
  65. uint queueCount = 0;
  66. if (!string.IsNullOrWhiteSpace(preferredDevice))
  67. {
  68. var physicalDevice = SuitableDevices.FirstOrDefault(x => VulkanInitialization.StringFromIdPair(x.Value.VendorID, x.Value.DeviceID) == preferredDevice);
  69. queueFamilyIndex = FindSuitableQueueFamily(instance.Api, physicalDevice.Key,
  70. surface.ApiHandle, out queueCount);
  71. if (queueFamilyIndex != int.MaxValue)
  72. {
  73. return new VulkanPhysicalDevice(physicalDevice.Key, instance.Api, queueCount, queueFamilyIndex);
  74. }
  75. }
  76. if (preferDiscreteGpu)
  77. {
  78. var discreteGpus = SuitableDevices.Where(p => p.Value.DeviceType == PhysicalDeviceType.DiscreteGpu);
  79. foreach (var gpu in discreteGpus)
  80. {
  81. queueFamilyIndex = FindSuitableQueueFamily(instance.Api, gpu.Key,
  82. surface.ApiHandle, out queueCount);
  83. if (queueFamilyIndex != int.MaxValue)
  84. {
  85. return new VulkanPhysicalDevice(gpu.Key, instance.Api, queueCount, queueFamilyIndex);
  86. }
  87. }
  88. }
  89. foreach (var physicalDevice in SuitableDevices)
  90. {
  91. queueFamilyIndex = FindSuitableQueueFamily(instance.Api, physicalDevice.Key,
  92. surface.ApiHandle, out queueCount);
  93. if (queueFamilyIndex != int.MaxValue)
  94. {
  95. return new VulkanPhysicalDevice(physicalDevice.Key, instance.Api, queueCount, queueFamilyIndex);
  96. }
  97. }
  98. throw new Exception("No suitable physical device found");
  99. }
  100. private static unsafe bool IsSuitableDevice(Vk api, PhysicalDevice physicalDevice, PhysicalDeviceProperties properties, SurfaceKHR surface,
  101. out uint queueCount, out uint familyIndex)
  102. {
  103. queueCount = 0;
  104. familyIndex = 0;
  105. if (properties.DeviceType == PhysicalDeviceType.Cpu) return false;
  106. var extensionMatches = 0;
  107. uint propertiesCount;
  108. api.EnumerateDeviceExtensionProperties(physicalDevice, (byte*)null, &propertiesCount, null).ThrowOnError();
  109. var extensionProperties = new ExtensionProperties[propertiesCount];
  110. fixed (ExtensionProperties* pExtensionProperties = extensionProperties)
  111. {
  112. api.EnumerateDeviceExtensionProperties(
  113. physicalDevice,
  114. (byte*)null,
  115. &propertiesCount,
  116. pExtensionProperties).ThrowOnError();
  117. for (var i = 0; i < propertiesCount; i++)
  118. {
  119. var extensionName = Marshal.PtrToStringAnsi((IntPtr)pExtensionProperties[i].ExtensionName);
  120. if (VulkanInitialization.RequiredExtensions.Contains(extensionName))
  121. {
  122. extensionMatches++;
  123. }
  124. }
  125. }
  126. if (extensionMatches == VulkanInitialization.RequiredExtensions.Length)
  127. {
  128. familyIndex = FindSuitableQueueFamily(api, physicalDevice, surface, out queueCount);
  129. return familyIndex != uint.MaxValue;
  130. }
  131. return false;
  132. }
  133. internal unsafe string[] GetSupportedExtensions()
  134. {
  135. uint propertiesCount;
  136. Api.EnumerateDeviceExtensionProperties(InternalHandle, (byte*)null, &propertiesCount, null).ThrowOnError();
  137. var extensionProperties = new ExtensionProperties[propertiesCount];
  138. fixed (ExtensionProperties* pExtensionProperties = extensionProperties)
  139. {
  140. Api.EnumerateDeviceExtensionProperties(InternalHandle, (byte*)null, &propertiesCount, pExtensionProperties)
  141. .ThrowOnError();
  142. }
  143. return extensionProperties.Select(x => Marshal.PtrToStringAnsi((IntPtr)x.ExtensionName)).ToArray();
  144. }
  145. private static uint FindSuitableQueueFamily(Vk api, PhysicalDevice physicalDevice, SurfaceKHR surface,
  146. out uint queueCount)
  147. {
  148. const QueueFlags RequiredFlags = QueueFlags.QueueGraphicsBit | QueueFlags.QueueComputeBit;
  149. var khrSurface = new KhrSurface(api.Context);
  150. uint propertiesCount;
  151. api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, &propertiesCount, null);
  152. var properties = new QueueFamilyProperties[propertiesCount];
  153. fixed (QueueFamilyProperties* pProperties = properties)
  154. {
  155. api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, &propertiesCount, pProperties);
  156. }
  157. for (uint index = 0; index < propertiesCount; index++)
  158. {
  159. var queueFlags = properties[index].QueueFlags;
  160. khrSurface.GetPhysicalDeviceSurfaceSupport(physicalDevice, index, surface, out var surfaceSupported)
  161. .ThrowOnError();
  162. if (queueFlags.HasFlag(RequiredFlags) && surfaceSupported)
  163. {
  164. queueCount = properties[index].QueueCount;
  165. return index;
  166. }
  167. }
  168. queueCount = 0;
  169. return uint.MaxValue;
  170. }
  171. }
  172. }