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