VulkanRenderer.cs 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971
  1. using Ryujinx.Common.Configuration;
  2. using Ryujinx.Common.Logging;
  3. using Ryujinx.Graphics.GAL;
  4. using Ryujinx.Graphics.Shader;
  5. using Ryujinx.Graphics.Shader.Translation;
  6. using Ryujinx.Graphics.Vulkan.MoltenVK;
  7. using Ryujinx.Graphics.Vulkan.Queries;
  8. using Silk.NET.Vulkan;
  9. using Silk.NET.Vulkan.Extensions.EXT;
  10. using Silk.NET.Vulkan.Extensions.KHR;
  11. using System;
  12. using System.Collections.Generic;
  13. using System.Runtime.InteropServices;
  14. using Format = Ryujinx.Graphics.GAL.Format;
  15. using PrimitiveTopology = Ryujinx.Graphics.GAL.PrimitiveTopology;
  16. using SamplerCreateInfo = Ryujinx.Graphics.GAL.SamplerCreateInfo;
  17. namespace Ryujinx.Graphics.Vulkan
  18. {
  19. public sealed class VulkanRenderer : IRenderer
  20. {
  21. private VulkanInstance _instance;
  22. private SurfaceKHR _surface;
  23. private VulkanPhysicalDevice _physicalDevice;
  24. private Device _device;
  25. private WindowBase _window;
  26. private bool _initialized;
  27. internal FormatCapabilities FormatCapabilities { get; private set; }
  28. internal HardwareCapabilities Capabilities;
  29. internal Vk Api { get; private set; }
  30. internal KhrSurface SurfaceApi { get; private set; }
  31. internal KhrSwapchain SwapchainApi { get; private set; }
  32. internal ExtConditionalRendering ConditionalRenderingApi { get; private set; }
  33. internal ExtExtendedDynamicState ExtendedDynamicStateApi { get; private set; }
  34. internal KhrPushDescriptor PushDescriptorApi { get; private set; }
  35. internal ExtTransformFeedback TransformFeedbackApi { get; private set; }
  36. internal KhrDrawIndirectCount DrawIndirectCountApi { get; private set; }
  37. internal uint QueueFamilyIndex { get; private set; }
  38. internal Queue Queue { get; private set; }
  39. internal Queue BackgroundQueue { get; private set; }
  40. internal object BackgroundQueueLock { get; private set; }
  41. internal object QueueLock { get; private set; }
  42. internal MemoryAllocator MemoryAllocator { get; private set; }
  43. internal HostMemoryAllocator HostMemoryAllocator { get; private set; }
  44. internal CommandBufferPool CommandBufferPool { get; private set; }
  45. internal PipelineLayoutCache PipelineLayoutCache { get; private set; }
  46. internal BackgroundResources BackgroundResources { get; private set; }
  47. internal Action<Action> InterruptAction { get; private set; }
  48. internal SyncManager SyncManager { get; private set; }
  49. internal BufferManager BufferManager { get; private set; }
  50. internal HashSet<ShaderCollection> Shaders { get; }
  51. internal HashSet<ITexture> Textures { get; }
  52. internal HashSet<SamplerHolder> Samplers { get; }
  53. private VulkanDebugMessenger _debugMessenger;
  54. private Counters _counters;
  55. private PipelineFull _pipeline;
  56. internal HelperShader HelperShader { get; private set; }
  57. internal PipelineFull PipelineInternal => _pipeline;
  58. internal BarrierBatch Barriers { get; private set; }
  59. public IPipeline Pipeline => _pipeline;
  60. public IWindow Window => _window;
  61. private readonly Func<Instance, Vk, SurfaceKHR> _getSurface;
  62. private readonly Func<string[]> _getRequiredExtensions;
  63. private readonly string _preferredGpuId;
  64. private int[] _pdReservedBindings;
  65. private readonly static int[] _pdReservedBindingsNvn = { 3, 18, 21, 36, 30 };
  66. private readonly static int[] _pdReservedBindingsOgl = { 17, 18, 34, 35, 36 };
  67. internal Vendor Vendor { get; private set; }
  68. internal bool IsAmdWindows { get; private set; }
  69. internal bool IsIntelWindows { get; private set; }
  70. internal bool IsAmdGcn { get; private set; }
  71. internal bool IsNvidiaPreTuring { get; private set; }
  72. internal bool IsIntelArc { get; private set; }
  73. internal bool IsMoltenVk { get; private set; }
  74. internal bool IsTBDR { get; private set; }
  75. internal bool IsSharedMemory { get; private set; }
  76. public string GpuVendor { get; private set; }
  77. public string GpuDriver { get; private set; }
  78. public string GpuRenderer { get; private set; }
  79. public string GpuVersion { get; private set; }
  80. public bool PreferThreading => true;
  81. public event EventHandler<ScreenCaptureImageInfo> ScreenCaptured;
  82. public VulkanRenderer(Vk api, Func<Instance, Vk, SurfaceKHR> surfaceFunc, Func<string[]> requiredExtensionsFunc, string preferredGpuId)
  83. {
  84. _getSurface = surfaceFunc;
  85. _getRequiredExtensions = requiredExtensionsFunc;
  86. _preferredGpuId = preferredGpuId;
  87. Api = api;
  88. Shaders = new HashSet<ShaderCollection>();
  89. Textures = new HashSet<ITexture>();
  90. Samplers = new HashSet<SamplerHolder>();
  91. if (OperatingSystem.IsMacOS())
  92. {
  93. MVKInitialization.Initialize();
  94. // Any device running on MacOS is using MoltenVK, even Intel and AMD vendors.
  95. IsMoltenVk = true;
  96. }
  97. }
  98. private unsafe void LoadFeatures(uint maxQueueCount, uint queueFamilyIndex)
  99. {
  100. FormatCapabilities = new FormatCapabilities(Api, _physicalDevice.PhysicalDevice);
  101. if (Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtConditionalRendering conditionalRenderingApi))
  102. {
  103. ConditionalRenderingApi = conditionalRenderingApi;
  104. }
  105. if (Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtExtendedDynamicState extendedDynamicStateApi))
  106. {
  107. ExtendedDynamicStateApi = extendedDynamicStateApi;
  108. }
  109. if (Api.TryGetDeviceExtension(_instance.Instance, _device, out KhrPushDescriptor pushDescriptorApi))
  110. {
  111. PushDescriptorApi = pushDescriptorApi;
  112. }
  113. if (Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtTransformFeedback transformFeedbackApi))
  114. {
  115. TransformFeedbackApi = transformFeedbackApi;
  116. }
  117. if (Api.TryGetDeviceExtension(_instance.Instance, _device, out KhrDrawIndirectCount drawIndirectCountApi))
  118. {
  119. DrawIndirectCountApi = drawIndirectCountApi;
  120. }
  121. if (maxQueueCount >= 2)
  122. {
  123. Api.GetDeviceQueue(_device, queueFamilyIndex, 1, out var backgroundQueue);
  124. BackgroundQueue = backgroundQueue;
  125. BackgroundQueueLock = new object();
  126. }
  127. PhysicalDeviceProperties2 properties2 = new()
  128. {
  129. SType = StructureType.PhysicalDeviceProperties2,
  130. };
  131. PhysicalDeviceSubgroupProperties propertiesSubgroup = new()
  132. {
  133. SType = StructureType.PhysicalDeviceSubgroupProperties,
  134. PNext = properties2.PNext,
  135. };
  136. properties2.PNext = &propertiesSubgroup;
  137. PhysicalDeviceBlendOperationAdvancedPropertiesEXT propertiesBlendOperationAdvanced = new()
  138. {
  139. SType = StructureType.PhysicalDeviceBlendOperationAdvancedPropertiesExt,
  140. };
  141. bool supportsBlendOperationAdvanced = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_blend_operation_advanced");
  142. if (supportsBlendOperationAdvanced)
  143. {
  144. propertiesBlendOperationAdvanced.PNext = properties2.PNext;
  145. properties2.PNext = &propertiesBlendOperationAdvanced;
  146. }
  147. bool supportsTransformFeedback = _physicalDevice.IsDeviceExtensionPresent(ExtTransformFeedback.ExtensionName);
  148. PhysicalDeviceTransformFeedbackPropertiesEXT propertiesTransformFeedback = new()
  149. {
  150. SType = StructureType.PhysicalDeviceTransformFeedbackPropertiesExt,
  151. };
  152. if (supportsTransformFeedback)
  153. {
  154. propertiesTransformFeedback.PNext = properties2.PNext;
  155. properties2.PNext = &propertiesTransformFeedback;
  156. }
  157. PhysicalDevicePortabilitySubsetPropertiesKHR propertiesPortabilitySubset = new()
  158. {
  159. SType = StructureType.PhysicalDevicePortabilitySubsetPropertiesKhr,
  160. };
  161. bool supportsPushDescriptors = _physicalDevice.IsDeviceExtensionPresent(KhrPushDescriptor.ExtensionName);
  162. PhysicalDevicePushDescriptorPropertiesKHR propertiesPushDescriptor = new PhysicalDevicePushDescriptorPropertiesKHR()
  163. {
  164. SType = StructureType.PhysicalDevicePushDescriptorPropertiesKhr
  165. };
  166. if (supportsPushDescriptors)
  167. {
  168. propertiesPushDescriptor.PNext = properties2.PNext;
  169. properties2.PNext = &propertiesPushDescriptor;
  170. }
  171. PhysicalDeviceFeatures2 features2 = new()
  172. {
  173. SType = StructureType.PhysicalDeviceFeatures2,
  174. };
  175. PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT featuresPrimitiveTopologyListRestart = new()
  176. {
  177. SType = StructureType.PhysicalDevicePrimitiveTopologyListRestartFeaturesExt,
  178. };
  179. PhysicalDeviceRobustness2FeaturesEXT featuresRobustness2 = new()
  180. {
  181. SType = StructureType.PhysicalDeviceRobustness2FeaturesExt,
  182. };
  183. PhysicalDeviceShaderFloat16Int8FeaturesKHR featuresShaderInt8 = new()
  184. {
  185. SType = StructureType.PhysicalDeviceShaderFloat16Int8Features,
  186. };
  187. PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColor = new()
  188. {
  189. SType = StructureType.PhysicalDeviceCustomBorderColorFeaturesExt,
  190. };
  191. PhysicalDeviceDepthClipControlFeaturesEXT featuresDepthClipControl = new()
  192. {
  193. SType = StructureType.PhysicalDeviceDepthClipControlFeaturesExt,
  194. };
  195. PhysicalDevicePortabilitySubsetFeaturesKHR featuresPortabilitySubset = new()
  196. {
  197. SType = StructureType.PhysicalDevicePortabilitySubsetFeaturesKhr,
  198. };
  199. if (_physicalDevice.IsDeviceExtensionPresent("VK_EXT_primitive_topology_list_restart"))
  200. {
  201. features2.PNext = &featuresPrimitiveTopologyListRestart;
  202. }
  203. if (_physicalDevice.IsDeviceExtensionPresent("VK_EXT_robustness2"))
  204. {
  205. featuresRobustness2.PNext = features2.PNext;
  206. features2.PNext = &featuresRobustness2;
  207. }
  208. if (_physicalDevice.IsDeviceExtensionPresent("VK_KHR_shader_float16_int8"))
  209. {
  210. featuresShaderInt8.PNext = features2.PNext;
  211. features2.PNext = &featuresShaderInt8;
  212. }
  213. if (_physicalDevice.IsDeviceExtensionPresent("VK_EXT_custom_border_color"))
  214. {
  215. featuresCustomBorderColor.PNext = features2.PNext;
  216. features2.PNext = &featuresCustomBorderColor;
  217. }
  218. bool supportsDepthClipControl = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_depth_clip_control");
  219. if (supportsDepthClipControl)
  220. {
  221. featuresDepthClipControl.PNext = features2.PNext;
  222. features2.PNext = &featuresDepthClipControl;
  223. }
  224. bool usePortability = _physicalDevice.IsDeviceExtensionPresent("VK_KHR_portability_subset");
  225. if (usePortability)
  226. {
  227. propertiesPortabilitySubset.PNext = properties2.PNext;
  228. properties2.PNext = &propertiesPortabilitySubset;
  229. featuresPortabilitySubset.PNext = features2.PNext;
  230. features2.PNext = &featuresPortabilitySubset;
  231. }
  232. Api.GetPhysicalDeviceProperties2(_physicalDevice.PhysicalDevice, &properties2);
  233. Api.GetPhysicalDeviceFeatures2(_physicalDevice.PhysicalDevice, &features2);
  234. var portabilityFlags = PortabilitySubsetFlags.None;
  235. uint vertexBufferAlignment = 1;
  236. if (usePortability)
  237. {
  238. vertexBufferAlignment = propertiesPortabilitySubset.MinVertexInputBindingStrideAlignment;
  239. portabilityFlags |= featuresPortabilitySubset.TriangleFans ? 0 : PortabilitySubsetFlags.NoTriangleFans;
  240. portabilityFlags |= featuresPortabilitySubset.PointPolygons ? 0 : PortabilitySubsetFlags.NoPointMode;
  241. portabilityFlags |= featuresPortabilitySubset.ImageView2DOn3DImage ? 0 : PortabilitySubsetFlags.No3DImageView;
  242. portabilityFlags |= featuresPortabilitySubset.SamplerMipLodBias ? 0 : PortabilitySubsetFlags.NoLodBias;
  243. }
  244. bool supportsCustomBorderColor = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_custom_border_color") &&
  245. featuresCustomBorderColor.CustomBorderColors &&
  246. featuresCustomBorderColor.CustomBorderColorWithoutFormat;
  247. ref var properties = ref properties2.Properties;
  248. var hasDriverProperties = _physicalDevice.TryGetPhysicalDeviceDriverPropertiesKHR(Api, out var driverProperties);
  249. Vendor = VendorUtils.FromId(properties.VendorID);
  250. IsAmdWindows = Vendor == Vendor.Amd && OperatingSystem.IsWindows();
  251. IsIntelWindows = Vendor == Vendor.Intel && OperatingSystem.IsWindows();
  252. IsTBDR =
  253. Vendor == Vendor.Apple ||
  254. Vendor == Vendor.Qualcomm ||
  255. Vendor == Vendor.ARM ||
  256. Vendor == Vendor.Broadcom ||
  257. Vendor == Vendor.ImgTec;
  258. GpuVendor = VendorUtils.GetNameFromId(properties.VendorID);
  259. GpuDriver = hasDriverProperties && !OperatingSystem.IsMacOS() ?
  260. VendorUtils.GetFriendlyDriverName(driverProperties.DriverID) : GpuVendor; // Fallback to vendor name if driver is unavailable or on MacOS where vendor is preferred.
  261. fixed (byte* deviceName = properties.DeviceName)
  262. {
  263. GpuRenderer = Marshal.PtrToStringAnsi((IntPtr)deviceName);
  264. }
  265. GpuVersion = $"Vulkan v{ParseStandardVulkanVersion(properties.ApiVersion)}, Driver v{ParseDriverVersion(ref properties)}";
  266. IsAmdGcn = !IsMoltenVk && Vendor == Vendor.Amd && VendorUtils.AmdGcnRegex().IsMatch(GpuRenderer);
  267. if (Vendor == Vendor.Nvidia)
  268. {
  269. var match = VendorUtils.NvidiaConsumerClassRegex().Match(GpuRenderer);
  270. if (match != null && int.TryParse(match.Groups[2].Value, out int gpuNumber))
  271. {
  272. IsNvidiaPreTuring = gpuNumber < 2000;
  273. }
  274. else if (GpuDriver.Contains("TITAN") && !GpuDriver.Contains("RTX"))
  275. {
  276. IsNvidiaPreTuring = true;
  277. }
  278. }
  279. else if (Vendor == Vendor.Intel)
  280. {
  281. IsIntelArc = GpuRenderer.StartsWith("Intel(R) Arc(TM)");
  282. }
  283. ulong minResourceAlignment = Math.Max(
  284. Math.Max(
  285. properties.Limits.MinStorageBufferOffsetAlignment,
  286. properties.Limits.MinUniformBufferOffsetAlignment),
  287. properties.Limits.MinTexelBufferOffsetAlignment
  288. );
  289. SampleCountFlags supportedSampleCounts =
  290. properties.Limits.FramebufferColorSampleCounts &
  291. properties.Limits.FramebufferDepthSampleCounts &
  292. properties.Limits.FramebufferStencilSampleCounts;
  293. Capabilities = new HardwareCapabilities(
  294. _physicalDevice.IsDeviceExtensionPresent("VK_EXT_index_type_uint8"),
  295. supportsCustomBorderColor,
  296. supportsBlendOperationAdvanced,
  297. propertiesBlendOperationAdvanced.AdvancedBlendCorrelatedOverlap,
  298. propertiesBlendOperationAdvanced.AdvancedBlendNonPremultipliedSrcColor,
  299. propertiesBlendOperationAdvanced.AdvancedBlendNonPremultipliedDstColor,
  300. _physicalDevice.IsDeviceExtensionPresent(KhrDrawIndirectCount.ExtensionName),
  301. _physicalDevice.IsDeviceExtensionPresent("VK_EXT_fragment_shader_interlock"),
  302. _physicalDevice.IsDeviceExtensionPresent("VK_NV_geometry_shader_passthrough"),
  303. features2.Features.ShaderFloat64,
  304. featuresShaderInt8.ShaderInt8,
  305. _physicalDevice.IsDeviceExtensionPresent("VK_EXT_shader_stencil_export"),
  306. features2.Features.ShaderStorageImageMultisample,
  307. _physicalDevice.IsDeviceExtensionPresent(ExtConditionalRendering.ExtensionName),
  308. _physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName),
  309. features2.Features.MultiViewport && !(IsMoltenVk && Vendor == Vendor.Amd), // Workaround for AMD on MoltenVK issue
  310. featuresRobustness2.NullDescriptor || IsMoltenVk,
  311. supportsPushDescriptors && !IsMoltenVk,
  312. propertiesPushDescriptor.MaxPushDescriptors,
  313. featuresPrimitiveTopologyListRestart.PrimitiveTopologyListRestart,
  314. featuresPrimitiveTopologyListRestart.PrimitiveTopologyPatchListRestart,
  315. supportsTransformFeedback,
  316. propertiesTransformFeedback.TransformFeedbackQueries,
  317. features2.Features.OcclusionQueryPrecise,
  318. _physicalDevice.PhysicalDeviceFeatures.PipelineStatisticsQuery,
  319. _physicalDevice.PhysicalDeviceFeatures.GeometryShader,
  320. _physicalDevice.PhysicalDeviceFeatures.TessellationShader,
  321. _physicalDevice.IsDeviceExtensionPresent("VK_NV_viewport_array2"),
  322. _physicalDevice.IsDeviceExtensionPresent(ExtExternalMemoryHost.ExtensionName),
  323. supportsDepthClipControl && featuresDepthClipControl.DepthClipControl,
  324. propertiesSubgroup.SubgroupSize,
  325. supportedSampleCounts,
  326. portabilityFlags,
  327. vertexBufferAlignment,
  328. properties.Limits.SubTexelPrecisionBits,
  329. minResourceAlignment);
  330. IsSharedMemory = MemoryAllocator.IsDeviceMemoryShared(_physicalDevice);
  331. MemoryAllocator = new MemoryAllocator(Api, _physicalDevice, _device);
  332. Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtExternalMemoryHost hostMemoryApi);
  333. HostMemoryAllocator = new HostMemoryAllocator(MemoryAllocator, Api, hostMemoryApi, _device);
  334. CommandBufferPool = new CommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex);
  335. PipelineLayoutCache = new PipelineLayoutCache();
  336. BackgroundResources = new BackgroundResources(this, _device);
  337. BufferManager = new BufferManager(this, _device);
  338. SyncManager = new SyncManager(this, _device);
  339. _pipeline = new PipelineFull(this, _device);
  340. _pipeline.Initialize();
  341. HelperShader = new HelperShader(this, _device);
  342. Barriers = new BarrierBatch(this);
  343. _counters = new Counters(this, _device, _pipeline);
  344. }
  345. private void SetupContext(GraphicsDebugLevel logLevel)
  346. {
  347. _instance = VulkanInitialization.CreateInstance(Api, logLevel, _getRequiredExtensions());
  348. _debugMessenger = new VulkanDebugMessenger(Api, _instance.Instance, logLevel);
  349. if (Api.TryGetInstanceExtension(_instance.Instance, out KhrSurface surfaceApi))
  350. {
  351. SurfaceApi = surfaceApi;
  352. }
  353. _surface = _getSurface(_instance.Instance, Api);
  354. _physicalDevice = VulkanInitialization.FindSuitablePhysicalDevice(Api, _instance, _surface, _preferredGpuId);
  355. var queueFamilyIndex = VulkanInitialization.FindSuitableQueueFamily(Api, _physicalDevice, _surface, out uint maxQueueCount);
  356. _device = VulkanInitialization.CreateDevice(Api, _physicalDevice, queueFamilyIndex, maxQueueCount);
  357. if (Api.TryGetDeviceExtension(_instance.Instance, _device, out KhrSwapchain swapchainApi))
  358. {
  359. SwapchainApi = swapchainApi;
  360. }
  361. Api.GetDeviceQueue(_device, queueFamilyIndex, 0, out var queue);
  362. Queue = queue;
  363. QueueLock = new object();
  364. LoadFeatures(maxQueueCount, queueFamilyIndex);
  365. QueueFamilyIndex = queueFamilyIndex;
  366. _window = new Window(this, _surface, _physicalDevice.PhysicalDevice, _device);
  367. _initialized = true;
  368. }
  369. internal int[] GetPushDescriptorReservedBindings(bool isOgl)
  370. {
  371. // The first call of this method determines what push descriptor layout is used for all shaders on this renderer.
  372. // This is chosen to minimize shaders that can't fit their uniforms on the device's max number of push descriptors.
  373. if (_pdReservedBindings == null)
  374. {
  375. if (Capabilities.MaxPushDescriptors <= Constants.MaxUniformBuffersPerStage * 2)
  376. {
  377. _pdReservedBindings = isOgl ? _pdReservedBindingsOgl : _pdReservedBindingsNvn;
  378. }
  379. else
  380. {
  381. _pdReservedBindings = Array.Empty<int>();
  382. }
  383. }
  384. return _pdReservedBindings;
  385. }
  386. public BufferHandle CreateBuffer(int size, BufferAccess access)
  387. {
  388. return BufferManager.CreateWithHandle(this, size, access.HasFlag(BufferAccess.SparseCompatible), access.Convert(), default, access == BufferAccess.Stream);
  389. }
  390. public BufferHandle CreateBuffer(int size, BufferAccess access, BufferHandle storageHint)
  391. {
  392. return BufferManager.CreateWithHandle(this, size, access.HasFlag(BufferAccess.SparseCompatible), access.Convert(), storageHint);
  393. }
  394. public BufferHandle CreateBuffer(nint pointer, int size)
  395. {
  396. return BufferManager.CreateHostImported(this, pointer, size);
  397. }
  398. public BufferHandle CreateBufferSparse(ReadOnlySpan<BufferRange> storageBuffers)
  399. {
  400. return BufferManager.CreateSparse(this, storageBuffers);
  401. }
  402. public IImageArray CreateImageArray(int size, bool isBuffer)
  403. {
  404. return new ImageArray(this, size, isBuffer);
  405. }
  406. public IProgram CreateProgram(ShaderSource[] sources, ShaderInfo info)
  407. {
  408. bool isCompute = sources.Length == 1 && sources[0].Stage == ShaderStage.Compute;
  409. if (info.State.HasValue || isCompute)
  410. {
  411. return new ShaderCollection(this, _device, sources, info.ResourceLayout, info.State ?? default, info.FromCache);
  412. }
  413. return new ShaderCollection(this, _device, sources, info.ResourceLayout);
  414. }
  415. internal ShaderCollection CreateProgramWithMinimalLayout(ShaderSource[] sources, ResourceLayout resourceLayout, SpecDescription[] specDescription = null)
  416. {
  417. return new ShaderCollection(this, _device, sources, resourceLayout, specDescription, isMinimal: true);
  418. }
  419. public ISampler CreateSampler(SamplerCreateInfo info)
  420. {
  421. return new SamplerHolder(this, _device, info);
  422. }
  423. public ITexture CreateTexture(TextureCreateInfo info)
  424. {
  425. if (info.Target == Target.TextureBuffer)
  426. {
  427. return new TextureBuffer(this, info);
  428. }
  429. return CreateTextureView(info);
  430. }
  431. public ITextureArray CreateTextureArray(int size, bool isBuffer)
  432. {
  433. return new TextureArray(this, size, isBuffer);
  434. }
  435. internal TextureView CreateTextureView(TextureCreateInfo info)
  436. {
  437. // This should be disposed when all views are destroyed.
  438. var storage = CreateTextureStorage(info);
  439. return storage.CreateView(info, 0, 0);
  440. }
  441. internal TextureStorage CreateTextureStorage(TextureCreateInfo info)
  442. {
  443. return new TextureStorage(this, _device, info);
  444. }
  445. public void DeleteBuffer(BufferHandle buffer)
  446. {
  447. BufferManager.Delete(buffer);
  448. }
  449. internal void FlushAllCommands()
  450. {
  451. _pipeline?.FlushCommandsImpl();
  452. }
  453. internal void RegisterFlush()
  454. {
  455. SyncManager.RegisterFlush();
  456. // Periodically free unused regions of the staging buffer to avoid doing it all at once.
  457. BufferManager.StagingBuffer.FreeCompleted();
  458. }
  459. public PinnedSpan<byte> GetBufferData(BufferHandle buffer, int offset, int size)
  460. {
  461. return BufferManager.GetData(buffer, offset, size);
  462. }
  463. public unsafe Capabilities GetCapabilities()
  464. {
  465. FormatFeatureFlags compressedFormatFeatureFlags =
  466. FormatFeatureFlags.SampledImageBit |
  467. FormatFeatureFlags.SampledImageFilterLinearBit |
  468. FormatFeatureFlags.BlitSrcBit |
  469. FormatFeatureFlags.TransferSrcBit |
  470. FormatFeatureFlags.TransferDstBit;
  471. bool supportsBc123CompressionFormat = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags,
  472. Format.Bc1RgbaSrgb,
  473. Format.Bc1RgbaUnorm,
  474. Format.Bc2Srgb,
  475. Format.Bc2Unorm,
  476. Format.Bc3Srgb,
  477. Format.Bc3Unorm);
  478. bool supportsBc45CompressionFormat = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags,
  479. Format.Bc4Snorm,
  480. Format.Bc4Unorm,
  481. Format.Bc5Snorm,
  482. Format.Bc5Unorm);
  483. bool supportsBc67CompressionFormat = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags,
  484. Format.Bc6HSfloat,
  485. Format.Bc6HUfloat,
  486. Format.Bc7Srgb,
  487. Format.Bc7Unorm);
  488. bool supportsEtc2CompressionFormat = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags,
  489. Format.Etc2RgbaSrgb,
  490. Format.Etc2RgbaUnorm,
  491. Format.Etc2RgbPtaSrgb,
  492. Format.Etc2RgbPtaUnorm,
  493. Format.Etc2RgbSrgb,
  494. Format.Etc2RgbUnorm);
  495. bool supports5BitComponentFormat = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags,
  496. Format.R5G6B5Unorm,
  497. Format.R5G5B5A1Unorm,
  498. Format.R5G5B5X1Unorm,
  499. Format.B5G6R5Unorm,
  500. Format.B5G5R5A1Unorm,
  501. Format.A1B5G5R5Unorm);
  502. bool supportsR4G4B4A4Format = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags,
  503. Format.R4G4B4A4Unorm);
  504. bool supportsAstcFormats = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags,
  505. Format.Astc4x4Unorm,
  506. Format.Astc5x4Unorm,
  507. Format.Astc5x5Unorm,
  508. Format.Astc6x5Unorm,
  509. Format.Astc6x6Unorm,
  510. Format.Astc8x5Unorm,
  511. Format.Astc8x6Unorm,
  512. Format.Astc8x8Unorm,
  513. Format.Astc10x5Unorm,
  514. Format.Astc10x6Unorm,
  515. Format.Astc10x8Unorm,
  516. Format.Astc10x10Unorm,
  517. Format.Astc12x10Unorm,
  518. Format.Astc12x12Unorm,
  519. Format.Astc4x4Srgb,
  520. Format.Astc5x4Srgb,
  521. Format.Astc5x5Srgb,
  522. Format.Astc6x5Srgb,
  523. Format.Astc6x6Srgb,
  524. Format.Astc8x5Srgb,
  525. Format.Astc8x6Srgb,
  526. Format.Astc8x8Srgb,
  527. Format.Astc10x5Srgb,
  528. Format.Astc10x6Srgb,
  529. Format.Astc10x8Srgb,
  530. Format.Astc10x10Srgb,
  531. Format.Astc12x10Srgb,
  532. Format.Astc12x12Srgb);
  533. PhysicalDeviceVulkan12Features featuresVk12 = new()
  534. {
  535. SType = StructureType.PhysicalDeviceVulkan12Features,
  536. };
  537. PhysicalDeviceFeatures2 features2 = new()
  538. {
  539. SType = StructureType.PhysicalDeviceFeatures2,
  540. PNext = &featuresVk12,
  541. };
  542. Api.GetPhysicalDeviceFeatures2(_physicalDevice.PhysicalDevice, &features2);
  543. var limits = _physicalDevice.PhysicalDeviceProperties.Limits;
  544. var mainQueueProperties = _physicalDevice.QueueFamilyProperties[QueueFamilyIndex];
  545. return new Capabilities(
  546. api: TargetApi.Vulkan,
  547. GpuVendor,
  548. hasFrontFacingBug: IsIntelWindows,
  549. hasVectorIndexingBug: Vendor == Vendor.Qualcomm,
  550. needsFragmentOutputSpecialization: IsMoltenVk,
  551. reduceShaderPrecision: IsMoltenVk,
  552. supportsAstcCompression: features2.Features.TextureCompressionAstcLdr && supportsAstcFormats,
  553. supportsBc123Compression: supportsBc123CompressionFormat,
  554. supportsBc45Compression: supportsBc45CompressionFormat,
  555. supportsBc67Compression: supportsBc67CompressionFormat,
  556. supportsEtc2Compression: supportsEtc2CompressionFormat,
  557. supports3DTextureCompression: true,
  558. supportsBgraFormat: true,
  559. supportsR4G4Format: false,
  560. supportsR4G4B4A4Format: supportsR4G4B4A4Format,
  561. supportsSnormBufferTextureFormat: true,
  562. supports5BitComponentFormat: supports5BitComponentFormat,
  563. supportsSparseBuffer: features2.Features.SparseBinding && mainQueueProperties.QueueFlags.HasFlag(QueueFlags.SparseBindingBit),
  564. supportsBlendEquationAdvanced: Capabilities.SupportsBlendEquationAdvanced,
  565. supportsFragmentShaderInterlock: Capabilities.SupportsFragmentShaderInterlock,
  566. supportsFragmentShaderOrderingIntel: false,
  567. supportsGeometryShader: Capabilities.SupportsGeometryShader,
  568. supportsGeometryShaderPassthrough: Capabilities.SupportsGeometryShaderPassthrough,
  569. supportsTransformFeedback: Capabilities.SupportsTransformFeedback,
  570. supportsImageLoadFormatted: features2.Features.ShaderStorageImageReadWithoutFormat,
  571. supportsLayerVertexTessellation: featuresVk12.ShaderOutputLayer,
  572. supportsMismatchingViewFormat: true,
  573. supportsCubemapView: !IsAmdGcn,
  574. supportsNonConstantTextureOffset: false,
  575. supportsScaledVertexFormats: FormatCapabilities.SupportsScaledVertexFormats(),
  576. supportsShaderBallot: false,
  577. supportsShaderBarrierDivergence: Vendor != Vendor.Intel,
  578. supportsShaderFloat64: Capabilities.SupportsShaderFloat64,
  579. supportsTextureGatherOffsets: features2.Features.ShaderImageGatherExtended && !IsMoltenVk,
  580. supportsTextureShadowLod: false,
  581. supportsVertexStoreAndAtomics: features2.Features.VertexPipelineStoresAndAtomics,
  582. supportsViewportIndexVertexTessellation: featuresVk12.ShaderOutputViewportIndex,
  583. supportsViewportMask: Capabilities.SupportsViewportArray2,
  584. supportsViewportSwizzle: false,
  585. supportsIndirectParameters: true,
  586. supportsDepthClipControl: Capabilities.SupportsDepthClipControl,
  587. maximumUniformBuffersPerStage: Constants.MaxUniformBuffersPerStage,
  588. maximumStorageBuffersPerStage: Constants.MaxStorageBuffersPerStage,
  589. maximumTexturesPerStage: Constants.MaxTexturesPerStage,
  590. maximumImagesPerStage: Constants.MaxImagesPerStage,
  591. maximumComputeSharedMemorySize: (int)limits.MaxComputeSharedMemorySize,
  592. maximumSupportedAnisotropy: (int)limits.MaxSamplerAnisotropy,
  593. shaderSubgroupSize: (int)Capabilities.SubgroupSize,
  594. storageBufferOffsetAlignment: (int)limits.MinStorageBufferOffsetAlignment,
  595. textureBufferOffsetAlignment: (int)limits.MinTexelBufferOffsetAlignment,
  596. gatherBiasPrecision: IsIntelWindows || IsAmdWindows ? (int)Capabilities.SubTexelPrecisionBits : 0);
  597. }
  598. public HardwareInfo GetHardwareInfo()
  599. {
  600. return new HardwareInfo(GpuVendor, GpuRenderer, GpuDriver);
  601. }
  602. /// <summary>
  603. /// Gets the available Vulkan devices using the default Vulkan API
  604. /// object returned by <see cref="Vk.GetApi()"/>
  605. /// </summary>
  606. /// <returns></returns>
  607. public static DeviceInfo[] GetPhysicalDevices()
  608. {
  609. try
  610. {
  611. return VulkanInitialization.GetSuitablePhysicalDevices(Vk.GetApi());
  612. }
  613. catch (Exception ex)
  614. {
  615. Logger.Error?.PrintMsg(LogClass.Gpu, $"Error querying Vulkan devices: {ex.Message}");
  616. return Array.Empty<DeviceInfo>();
  617. }
  618. }
  619. public static DeviceInfo[] GetPhysicalDevices(Vk api)
  620. {
  621. try
  622. {
  623. return VulkanInitialization.GetSuitablePhysicalDevices(api);
  624. }
  625. catch (Exception)
  626. {
  627. // If we got an exception here, Vulkan is most likely not supported.
  628. return Array.Empty<DeviceInfo>();
  629. }
  630. }
  631. private static string ParseStandardVulkanVersion(uint version)
  632. {
  633. return $"{version >> 22}.{(version >> 12) & 0x3FF}.{version & 0xFFF}";
  634. }
  635. private static string ParseDriverVersion(ref PhysicalDeviceProperties properties)
  636. {
  637. uint driverVersionRaw = properties.DriverVersion;
  638. // NVIDIA differ from the standard here and uses a different format.
  639. if (properties.VendorID == 0x10DE)
  640. {
  641. return $"{(driverVersionRaw >> 22) & 0x3FF}.{(driverVersionRaw >> 14) & 0xFF}.{(driverVersionRaw >> 6) & 0xFF}.{driverVersionRaw & 0x3F}";
  642. }
  643. return ParseStandardVulkanVersion(driverVersionRaw);
  644. }
  645. internal PrimitiveTopology TopologyRemap(PrimitiveTopology topology)
  646. {
  647. return topology switch
  648. {
  649. PrimitiveTopology.Quads => PrimitiveTopology.Triangles,
  650. PrimitiveTopology.QuadStrip => PrimitiveTopology.TriangleStrip,
  651. PrimitiveTopology.TriangleFan or PrimitiveTopology.Polygon => Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.NoTriangleFans)
  652. ? PrimitiveTopology.Triangles
  653. : topology,
  654. _ => topology,
  655. };
  656. }
  657. internal bool TopologyUnsupported(PrimitiveTopology topology)
  658. {
  659. return topology switch
  660. {
  661. PrimitiveTopology.Quads => true,
  662. PrimitiveTopology.TriangleFan or PrimitiveTopology.Polygon => Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.NoTriangleFans),
  663. _ => false,
  664. };
  665. }
  666. private void PrintGpuInformation()
  667. {
  668. Logger.Notice.Print(LogClass.Gpu, $"{GpuVendor} {GpuRenderer} ({GpuVersion})");
  669. }
  670. public void Initialize(GraphicsDebugLevel logLevel)
  671. {
  672. SetupContext(logLevel);
  673. PrintGpuInformation();
  674. }
  675. internal bool NeedsVertexBufferAlignment(int attrScalarAlignment, out int alignment)
  676. {
  677. if (Capabilities.VertexBufferAlignment > 1)
  678. {
  679. alignment = (int)Capabilities.VertexBufferAlignment;
  680. return true;
  681. }
  682. else if (Vendor != Vendor.Nvidia)
  683. {
  684. // Vulkan requires that vertex attributes are globally aligned by their component size,
  685. // so buffer strides that don't divide by the largest scalar element are invalid.
  686. // Guest applications do this, NVIDIA GPUs are OK with it, others are not.
  687. alignment = attrScalarAlignment;
  688. return true;
  689. }
  690. alignment = 1;
  691. return false;
  692. }
  693. public void PreFrame()
  694. {
  695. SyncManager.Cleanup();
  696. }
  697. public ICounterEvent ReportCounter(CounterType type, EventHandler<ulong> resultHandler, float divisor, bool hostReserved)
  698. {
  699. return _counters.QueueReport(type, resultHandler, divisor, hostReserved);
  700. }
  701. public void ResetCounter(CounterType type)
  702. {
  703. _counters.QueueReset(type);
  704. }
  705. public void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan<byte> data)
  706. {
  707. BufferManager.SetData(buffer, offset, data, _pipeline.CurrentCommandBuffer, _pipeline.EndRenderPassDelegate);
  708. }
  709. public void UpdateCounters()
  710. {
  711. _counters.Update();
  712. }
  713. public void ResetCounterPool()
  714. {
  715. _counters.ResetCounterPool();
  716. }
  717. public void ResetFutureCounters(CommandBuffer cmd, int count)
  718. {
  719. _counters?.ResetFutureCounters(cmd, count);
  720. }
  721. public void BackgroundContextAction(Action action, bool alwaysBackground = false)
  722. {
  723. action();
  724. }
  725. public void CreateSync(ulong id, bool strict)
  726. {
  727. SyncManager.Create(id, strict);
  728. }
  729. public IProgram LoadProgramBinary(byte[] programBinary, bool isFragment, ShaderInfo info)
  730. {
  731. throw new NotImplementedException();
  732. }
  733. public void WaitSync(ulong id)
  734. {
  735. SyncManager.Wait(id);
  736. }
  737. public ulong GetCurrentSync()
  738. {
  739. return SyncManager.GetCurrent();
  740. }
  741. public void SetInterruptAction(Action<Action> interruptAction)
  742. {
  743. InterruptAction = interruptAction;
  744. }
  745. public void Screenshot()
  746. {
  747. _window.ScreenCaptureRequested = true;
  748. }
  749. public void OnScreenCaptured(ScreenCaptureImageInfo bitmap)
  750. {
  751. ScreenCaptured?.Invoke(this, bitmap);
  752. }
  753. public unsafe void Dispose()
  754. {
  755. if (!_initialized)
  756. {
  757. return;
  758. }
  759. CommandBufferPool.Dispose();
  760. BackgroundResources.Dispose();
  761. _counters.Dispose();
  762. _window.Dispose();
  763. HelperShader.Dispose();
  764. _pipeline.Dispose();
  765. BufferManager.Dispose();
  766. PipelineLayoutCache.Dispose();
  767. Barriers.Dispose();
  768. MemoryAllocator.Dispose();
  769. foreach (var shader in Shaders)
  770. {
  771. shader.Dispose();
  772. }
  773. foreach (var texture in Textures)
  774. {
  775. texture.Release();
  776. }
  777. foreach (var sampler in Samplers)
  778. {
  779. sampler.Dispose();
  780. }
  781. SurfaceApi.DestroySurface(_instance.Instance, _surface, null);
  782. Api.DestroyDevice(_device, null);
  783. _debugMessenger.Dispose();
  784. // Last step destroy the instance
  785. _instance.Dispose();
  786. }
  787. public bool PrepareHostMapping(nint address, ulong size)
  788. {
  789. return Capabilities.SupportsHostImportedMemory &&
  790. HostMemoryAllocator.TryImport(BufferManager.HostImportedBufferMemoryRequirements, BufferManager.DefaultBufferMemoryFlags, address, size);
  791. }
  792. }
  793. }