VulkanInitialization.cs
1 using Ryujinx.Common.Configuration; 2 using Ryujinx.Common.Logging; 3 using Ryujinx.Graphics.GAL; 4 using Silk.NET.Vulkan; 5 using Silk.NET.Vulkan.Extensions.EXT; 6 using Silk.NET.Vulkan.Extensions.KHR; 7 using System; 8 using System.Collections.Generic; 9 using System.Linq; 10 using System.Runtime.InteropServices; 11 12 namespace Ryujinx.Graphics.Vulkan 13 { 14 public unsafe static class VulkanInitialization 15 { 16 private const uint InvalidIndex = uint.MaxValue; 17 private static readonly uint _minimalVulkanVersion = Vk.Version11.Value; 18 private static readonly uint _minimalInstanceVulkanVersion = Vk.Version12.Value; 19 private static readonly uint _maximumVulkanVersion = Vk.Version12.Value; 20 private const string AppName = "Ryujinx.Graphics.Vulkan"; 21 private const int QueuesCount = 2; 22 23 private static readonly string[] _desirableExtensions = { 24 ExtConditionalRendering.ExtensionName, 25 ExtExtendedDynamicState.ExtensionName, 26 ExtTransformFeedback.ExtensionName, 27 KhrDrawIndirectCount.ExtensionName, 28 KhrPushDescriptor.ExtensionName, 29 ExtExternalMemoryHost.ExtensionName, 30 "VK_EXT_blend_operation_advanced", 31 "VK_EXT_custom_border_color", 32 "VK_EXT_descriptor_indexing", // Enabling this works around an issue with disposed buffer bindings on RADV. 33 "VK_EXT_fragment_shader_interlock", 34 "VK_EXT_index_type_uint8", 35 "VK_EXT_primitive_topology_list_restart", 36 "VK_EXT_robustness2", 37 "VK_EXT_shader_stencil_export", 38 "VK_KHR_shader_float16_int8", 39 "VK_EXT_shader_subgroup_ballot", 40 "VK_NV_geometry_shader_passthrough", 41 "VK_NV_viewport_array2", 42 "VK_EXT_depth_clip_control", 43 "VK_KHR_portability_subset", // As per spec, we should enable this if present. 44 "VK_EXT_4444_formats", 45 "VK_KHR_8bit_storage", 46 "VK_KHR_maintenance2", 47 "VK_EXT_attachment_feedback_loop_layout", 48 "VK_EXT_attachment_feedback_loop_dynamic_state", 49 }; 50 51 private static readonly string[] _requiredExtensions = { 52 KhrSwapchain.ExtensionName, 53 }; 54 55 internal static VulkanInstance CreateInstance(Vk api, GraphicsDebugLevel logLevel, string[] requiredExtensions) 56 { 57 var enabledLayers = new List<string>(); 58 59 var instanceExtensions = VulkanInstance.GetInstanceExtensions(api); 60 var instanceLayers = VulkanInstance.GetInstanceLayers(api); 61 62 void AddAvailableLayer(string layerName) 63 { 64 if (instanceLayers.Contains(layerName)) 65 { 66 enabledLayers.Add(layerName); 67 } 68 else 69 { 70 Logger.Warning?.Print(LogClass.Gpu, $"Missing layer {layerName}"); 71 } 72 } 73 74 if (logLevel != GraphicsDebugLevel.None) 75 { 76 AddAvailableLayer("VK_LAYER_KHRONOS_validation"); 77 } 78 79 var enabledExtensions = requiredExtensions; 80 81 if (instanceExtensions.Contains("VK_EXT_debug_utils")) 82 { 83 enabledExtensions = enabledExtensions.Append(ExtDebugUtils.ExtensionName).ToArray(); 84 } 85 86 var appName = Marshal.StringToHGlobalAnsi(AppName); 87 88 var applicationInfo = new ApplicationInfo 89 { 90 PApplicationName = (byte*)appName, 91 ApplicationVersion = 1, 92 PEngineName = (byte*)appName, 93 EngineVersion = 1, 94 ApiVersion = _maximumVulkanVersion, 95 }; 96 97 IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length]; 98 IntPtr* ppEnabledLayers = stackalloc IntPtr[enabledLayers.Count]; 99 100 for (int i = 0; i < enabledExtensions.Length; i++) 101 { 102 ppEnabledExtensions[i] = Marshal.StringToHGlobalAnsi(enabledExtensions[i]); 103 } 104 105 for (int i = 0; i < enabledLayers.Count; i++) 106 { 107 ppEnabledLayers[i] = Marshal.StringToHGlobalAnsi(enabledLayers[i]); 108 } 109 110 var instanceCreateInfo = new InstanceCreateInfo 111 { 112 SType = StructureType.InstanceCreateInfo, 113 PApplicationInfo = &applicationInfo, 114 PpEnabledExtensionNames = (byte**)ppEnabledExtensions, 115 PpEnabledLayerNames = (byte**)ppEnabledLayers, 116 EnabledExtensionCount = (uint)enabledExtensions.Length, 117 EnabledLayerCount = (uint)enabledLayers.Count, 118 }; 119 120 Result result = VulkanInstance.Create(api, ref instanceCreateInfo, out var instance); 121 122 Marshal.FreeHGlobal(appName); 123 124 for (int i = 0; i < enabledExtensions.Length; i++) 125 { 126 Marshal.FreeHGlobal(ppEnabledExtensions[i]); 127 } 128 129 for (int i = 0; i < enabledLayers.Count; i++) 130 { 131 Marshal.FreeHGlobal(ppEnabledLayers[i]); 132 } 133 134 result.ThrowOnError(); 135 136 return instance; 137 } 138 139 internal static VulkanPhysicalDevice FindSuitablePhysicalDevice(Vk api, VulkanInstance instance, SurfaceKHR surface, string preferredGpuId) 140 { 141 instance.EnumeratePhysicalDevices(out var physicalDevices).ThrowOnError(); 142 143 // First we try to pick the user preferred GPU. 144 for (int i = 0; i < physicalDevices.Length; i++) 145 { 146 if (IsPreferredAndSuitableDevice(api, physicalDevices[i], surface, preferredGpuId)) 147 { 148 return physicalDevices[i]; 149 } 150 } 151 152 // If we fail to do that, just use the first compatible GPU. 153 for (int i = 0; i < physicalDevices.Length; i++) 154 { 155 if (IsSuitableDevice(api, physicalDevices[i], surface)) 156 { 157 return physicalDevices[i]; 158 } 159 } 160 161 throw new VulkanException("Initialization failed, none of the available GPUs meets the minimum requirements."); 162 } 163 164 internal static DeviceInfo[] GetSuitablePhysicalDevices(Vk api) 165 { 166 var appName = Marshal.StringToHGlobalAnsi(AppName); 167 168 var applicationInfo = new ApplicationInfo 169 { 170 PApplicationName = (byte*)appName, 171 ApplicationVersion = 1, 172 PEngineName = (byte*)appName, 173 EngineVersion = 1, 174 ApiVersion = _maximumVulkanVersion, 175 }; 176 177 var instanceCreateInfo = new InstanceCreateInfo 178 { 179 SType = StructureType.InstanceCreateInfo, 180 PApplicationInfo = &applicationInfo, 181 PpEnabledExtensionNames = null, 182 PpEnabledLayerNames = null, 183 EnabledExtensionCount = 0, 184 EnabledLayerCount = 0, 185 }; 186 187 Result result = VulkanInstance.Create(api, ref instanceCreateInfo, out var rawInstance); 188 189 Marshal.FreeHGlobal(appName); 190 191 result.ThrowOnError(); 192 193 using VulkanInstance instance = rawInstance; 194 195 // We currently assume that the instance is compatible with Vulkan 1.2 196 // TODO: Remove this once we relax our initialization codepaths. 197 if (instance.InstanceVersion < _minimalInstanceVulkanVersion) 198 { 199 return Array.Empty<DeviceInfo>(); 200 } 201 202 instance.EnumeratePhysicalDevices(out VulkanPhysicalDevice[] physicalDevices).ThrowOnError(); 203 204 List<DeviceInfo> deviceInfos = new(); 205 206 foreach (VulkanPhysicalDevice physicalDevice in physicalDevices) 207 { 208 if (physicalDevice.PhysicalDeviceProperties.ApiVersion < _minimalVulkanVersion) 209 { 210 continue; 211 } 212 213 deviceInfos.Add(physicalDevice.ToDeviceInfo()); 214 } 215 216 return deviceInfos.ToArray(); 217 } 218 219 private static bool IsPreferredAndSuitableDevice(Vk api, VulkanPhysicalDevice physicalDevice, SurfaceKHR surface, string preferredGpuId) 220 { 221 if (physicalDevice.Id != preferredGpuId) 222 { 223 return false; 224 } 225 226 return IsSuitableDevice(api, physicalDevice, surface); 227 } 228 229 private static bool IsSuitableDevice(Vk api, VulkanPhysicalDevice physicalDevice, SurfaceKHR surface) 230 { 231 int extensionMatches = 0; 232 233 foreach (string requiredExtension in _requiredExtensions) 234 { 235 if (physicalDevice.IsDeviceExtensionPresent(requiredExtension)) 236 { 237 extensionMatches++; 238 } 239 } 240 241 return extensionMatches == _requiredExtensions.Length && FindSuitableQueueFamily(api, physicalDevice, surface, out _) != InvalidIndex; 242 } 243 244 internal static uint FindSuitableQueueFamily(Vk api, VulkanPhysicalDevice physicalDevice, SurfaceKHR surface, out uint queueCount) 245 { 246 const QueueFlags RequiredFlags = QueueFlags.GraphicsBit | QueueFlags.ComputeBit; 247 248 var khrSurface = new KhrSurface(api.Context); 249 250 for (uint index = 0; index < physicalDevice.QueueFamilyProperties.Length; index++) 251 { 252 ref QueueFamilyProperties property = ref physicalDevice.QueueFamilyProperties[index]; 253 254 khrSurface.GetPhysicalDeviceSurfaceSupport(physicalDevice.PhysicalDevice, index, surface, out var surfaceSupported).ThrowOnError(); 255 256 if (property.QueueFlags.HasFlag(RequiredFlags) && surfaceSupported) 257 { 258 queueCount = property.QueueCount; 259 260 return index; 261 } 262 } 263 264 queueCount = 0; 265 266 return InvalidIndex; 267 } 268 269 internal static Device CreateDevice(Vk api, VulkanPhysicalDevice physicalDevice, uint queueFamilyIndex, uint queueCount) 270 { 271 if (queueCount > QueuesCount) 272 { 273 queueCount = QueuesCount; 274 } 275 276 float* queuePriorities = stackalloc float[(int)queueCount]; 277 278 for (int i = 0; i < queueCount; i++) 279 { 280 queuePriorities[i] = 1f; 281 } 282 283 var queueCreateInfo = new DeviceQueueCreateInfo 284 { 285 SType = StructureType.DeviceQueueCreateInfo, 286 QueueFamilyIndex = queueFamilyIndex, 287 QueueCount = queueCount, 288 PQueuePriorities = queuePriorities, 289 }; 290 291 bool useRobustBufferAccess = VendorUtils.FromId(physicalDevice.PhysicalDeviceProperties.VendorID) == Vendor.Nvidia; 292 293 PhysicalDeviceFeatures2 features2 = new() 294 { 295 SType = StructureType.PhysicalDeviceFeatures2, 296 }; 297 298 PhysicalDeviceVulkan11Features supportedFeaturesVk11 = new() 299 { 300 SType = StructureType.PhysicalDeviceVulkan11Features, 301 PNext = features2.PNext, 302 }; 303 304 features2.PNext = &supportedFeaturesVk11; 305 306 PhysicalDeviceCustomBorderColorFeaturesEXT supportedFeaturesCustomBorderColor = new() 307 { 308 SType = StructureType.PhysicalDeviceCustomBorderColorFeaturesExt, 309 PNext = features2.PNext, 310 }; 311 312 if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_custom_border_color")) 313 { 314 features2.PNext = &supportedFeaturesCustomBorderColor; 315 } 316 317 PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT supportedFeaturesPrimitiveTopologyListRestart = new() 318 { 319 SType = StructureType.PhysicalDevicePrimitiveTopologyListRestartFeaturesExt, 320 PNext = features2.PNext, 321 }; 322 323 if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_primitive_topology_list_restart")) 324 { 325 features2.PNext = &supportedFeaturesPrimitiveTopologyListRestart; 326 } 327 328 PhysicalDeviceTransformFeedbackFeaturesEXT supportedFeaturesTransformFeedback = new() 329 { 330 SType = StructureType.PhysicalDeviceTransformFeedbackFeaturesExt, 331 PNext = features2.PNext, 332 }; 333 334 if (physicalDevice.IsDeviceExtensionPresent(ExtTransformFeedback.ExtensionName)) 335 { 336 features2.PNext = &supportedFeaturesTransformFeedback; 337 } 338 339 PhysicalDeviceRobustness2FeaturesEXT supportedFeaturesRobustness2 = new() 340 { 341 SType = StructureType.PhysicalDeviceRobustness2FeaturesExt, 342 }; 343 344 if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_robustness2")) 345 { 346 supportedFeaturesRobustness2.PNext = features2.PNext; 347 348 features2.PNext = &supportedFeaturesRobustness2; 349 } 350 351 PhysicalDeviceDepthClipControlFeaturesEXT supportedFeaturesDepthClipControl = new() 352 { 353 SType = StructureType.PhysicalDeviceDepthClipControlFeaturesExt, 354 PNext = features2.PNext, 355 }; 356 357 if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_depth_clip_control")) 358 { 359 features2.PNext = &supportedFeaturesDepthClipControl; 360 } 361 362 PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT supportedFeaturesAttachmentFeedbackLoopLayout = new() 363 { 364 SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesExt, 365 PNext = features2.PNext, 366 }; 367 368 if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_layout")) 369 { 370 features2.PNext = &supportedFeaturesAttachmentFeedbackLoopLayout; 371 } 372 373 PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT supportedFeaturesDynamicAttachmentFeedbackLoopLayout = new() 374 { 375 SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesExt, 376 PNext = features2.PNext, 377 }; 378 379 if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_dynamic_state")) 380 { 381 features2.PNext = &supportedFeaturesDynamicAttachmentFeedbackLoopLayout; 382 } 383 384 PhysicalDeviceVulkan12Features supportedPhysicalDeviceVulkan12Features = new() 385 { 386 SType = StructureType.PhysicalDeviceVulkan12Features, 387 PNext = features2.PNext, 388 }; 389 390 features2.PNext = &supportedPhysicalDeviceVulkan12Features; 391 392 api.GetPhysicalDeviceFeatures2(physicalDevice.PhysicalDevice, &features2); 393 394 var supportedFeatures = features2.Features; 395 396 var features = new PhysicalDeviceFeatures 397 { 398 DepthBiasClamp = supportedFeatures.DepthBiasClamp, 399 DepthClamp = supportedFeatures.DepthClamp, 400 DualSrcBlend = supportedFeatures.DualSrcBlend, 401 FragmentStoresAndAtomics = supportedFeatures.FragmentStoresAndAtomics, 402 GeometryShader = supportedFeatures.GeometryShader, 403 ImageCubeArray = supportedFeatures.ImageCubeArray, 404 IndependentBlend = supportedFeatures.IndependentBlend, 405 LogicOp = supportedFeatures.LogicOp, 406 OcclusionQueryPrecise = supportedFeatures.OcclusionQueryPrecise, 407 MultiViewport = supportedFeatures.MultiViewport, 408 PipelineStatisticsQuery = supportedFeatures.PipelineStatisticsQuery, 409 SamplerAnisotropy = supportedFeatures.SamplerAnisotropy, 410 ShaderClipDistance = supportedFeatures.ShaderClipDistance, 411 ShaderFloat64 = supportedFeatures.ShaderFloat64, 412 ShaderImageGatherExtended = supportedFeatures.ShaderImageGatherExtended, 413 ShaderStorageImageMultisample = supportedFeatures.ShaderStorageImageMultisample, 414 ShaderStorageImageReadWithoutFormat = supportedFeatures.ShaderStorageImageReadWithoutFormat, 415 ShaderStorageImageWriteWithoutFormat = supportedFeatures.ShaderStorageImageWriteWithoutFormat, 416 TessellationShader = supportedFeatures.TessellationShader, 417 VertexPipelineStoresAndAtomics = supportedFeatures.VertexPipelineStoresAndAtomics, 418 RobustBufferAccess = useRobustBufferAccess, 419 SampleRateShading = supportedFeatures.SampleRateShading, 420 }; 421 422 void* pExtendedFeatures = null; 423 424 PhysicalDeviceTransformFeedbackFeaturesEXT featuresTransformFeedback; 425 426 if (physicalDevice.IsDeviceExtensionPresent(ExtTransformFeedback.ExtensionName)) 427 { 428 featuresTransformFeedback = new PhysicalDeviceTransformFeedbackFeaturesEXT 429 { 430 SType = StructureType.PhysicalDeviceTransformFeedbackFeaturesExt, 431 PNext = pExtendedFeatures, 432 TransformFeedback = supportedFeaturesTransformFeedback.TransformFeedback, 433 }; 434 435 pExtendedFeatures = &featuresTransformFeedback; 436 } 437 438 PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT featuresPrimitiveTopologyListRestart; 439 440 if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_primitive_topology_list_restart")) 441 { 442 featuresPrimitiveTopologyListRestart = new PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT 443 { 444 SType = StructureType.PhysicalDevicePrimitiveTopologyListRestartFeaturesExt, 445 PNext = pExtendedFeatures, 446 PrimitiveTopologyListRestart = supportedFeaturesPrimitiveTopologyListRestart.PrimitiveTopologyListRestart, 447 PrimitiveTopologyPatchListRestart = supportedFeaturesPrimitiveTopologyListRestart.PrimitiveTopologyPatchListRestart, 448 }; 449 450 pExtendedFeatures = &featuresPrimitiveTopologyListRestart; 451 } 452 453 PhysicalDeviceRobustness2FeaturesEXT featuresRobustness2; 454 455 if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_robustness2")) 456 { 457 featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT 458 { 459 SType = StructureType.PhysicalDeviceRobustness2FeaturesExt, 460 PNext = pExtendedFeatures, 461 NullDescriptor = supportedFeaturesRobustness2.NullDescriptor, 462 }; 463 464 pExtendedFeatures = &featuresRobustness2; 465 } 466 467 var featuresExtendedDynamicState = new PhysicalDeviceExtendedDynamicStateFeaturesEXT 468 { 469 SType = StructureType.PhysicalDeviceExtendedDynamicStateFeaturesExt, 470 PNext = pExtendedFeatures, 471 ExtendedDynamicState = physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName), 472 }; 473 474 pExtendedFeatures = &featuresExtendedDynamicState; 475 476 var featuresVk11 = new PhysicalDeviceVulkan11Features 477 { 478 SType = StructureType.PhysicalDeviceVulkan11Features, 479 PNext = pExtendedFeatures, 480 ShaderDrawParameters = supportedFeaturesVk11.ShaderDrawParameters, 481 }; 482 483 pExtendedFeatures = &featuresVk11; 484 485 var featuresVk12 = new PhysicalDeviceVulkan12Features 486 { 487 SType = StructureType.PhysicalDeviceVulkan12Features, 488 PNext = pExtendedFeatures, 489 DescriptorIndexing = supportedPhysicalDeviceVulkan12Features.DescriptorIndexing, 490 DrawIndirectCount = supportedPhysicalDeviceVulkan12Features.DrawIndirectCount, 491 UniformBufferStandardLayout = supportedPhysicalDeviceVulkan12Features.UniformBufferStandardLayout, 492 UniformAndStorageBuffer8BitAccess = supportedPhysicalDeviceVulkan12Features.UniformAndStorageBuffer8BitAccess, 493 StorageBuffer8BitAccess = supportedPhysicalDeviceVulkan12Features.StorageBuffer8BitAccess, 494 }; 495 496 pExtendedFeatures = &featuresVk12; 497 498 PhysicalDeviceIndexTypeUint8FeaturesEXT featuresIndexU8; 499 500 if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_index_type_uint8")) 501 { 502 featuresIndexU8 = new PhysicalDeviceIndexTypeUint8FeaturesEXT 503 { 504 SType = StructureType.PhysicalDeviceIndexTypeUint8FeaturesExt, 505 PNext = pExtendedFeatures, 506 IndexTypeUint8 = true, 507 }; 508 509 pExtendedFeatures = &featuresIndexU8; 510 } 511 512 PhysicalDeviceFragmentShaderInterlockFeaturesEXT featuresFragmentShaderInterlock; 513 514 if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_fragment_shader_interlock")) 515 { 516 featuresFragmentShaderInterlock = new PhysicalDeviceFragmentShaderInterlockFeaturesEXT 517 { 518 SType = StructureType.PhysicalDeviceFragmentShaderInterlockFeaturesExt, 519 PNext = pExtendedFeatures, 520 FragmentShaderPixelInterlock = true, 521 }; 522 523 pExtendedFeatures = &featuresFragmentShaderInterlock; 524 } 525 526 PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColor; 527 528 if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_custom_border_color") && 529 supportedFeaturesCustomBorderColor.CustomBorderColors && 530 supportedFeaturesCustomBorderColor.CustomBorderColorWithoutFormat) 531 { 532 featuresCustomBorderColor = new PhysicalDeviceCustomBorderColorFeaturesEXT 533 { 534 SType = StructureType.PhysicalDeviceCustomBorderColorFeaturesExt, 535 PNext = pExtendedFeatures, 536 CustomBorderColors = true, 537 CustomBorderColorWithoutFormat = true, 538 }; 539 540 pExtendedFeatures = &featuresCustomBorderColor; 541 } 542 543 PhysicalDeviceDepthClipControlFeaturesEXT featuresDepthClipControl; 544 545 if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_depth_clip_control") && 546 supportedFeaturesDepthClipControl.DepthClipControl) 547 { 548 featuresDepthClipControl = new PhysicalDeviceDepthClipControlFeaturesEXT 549 { 550 SType = StructureType.PhysicalDeviceDepthClipControlFeaturesExt, 551 PNext = pExtendedFeatures, 552 DepthClipControl = true, 553 }; 554 555 pExtendedFeatures = &featuresDepthClipControl; 556 } 557 558 PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT featuresAttachmentFeedbackLoopLayout; 559 560 if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_layout") && 561 supportedFeaturesAttachmentFeedbackLoopLayout.AttachmentFeedbackLoopLayout) 562 { 563 featuresAttachmentFeedbackLoopLayout = new() 564 { 565 SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesExt, 566 PNext = pExtendedFeatures, 567 AttachmentFeedbackLoopLayout = true, 568 }; 569 570 pExtendedFeatures = &featuresAttachmentFeedbackLoopLayout; 571 } 572 573 PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT featuresDynamicAttachmentFeedbackLoopLayout; 574 575 if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_dynamic_state") && 576 supportedFeaturesDynamicAttachmentFeedbackLoopLayout.AttachmentFeedbackLoopDynamicState) 577 { 578 featuresDynamicAttachmentFeedbackLoopLayout = new() 579 { 580 SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesExt, 581 PNext = pExtendedFeatures, 582 AttachmentFeedbackLoopDynamicState = true, 583 }; 584 585 pExtendedFeatures = &featuresDynamicAttachmentFeedbackLoopLayout; 586 } 587 588 var enabledExtensions = _requiredExtensions.Union(_desirableExtensions.Intersect(physicalDevice.DeviceExtensions)).ToArray(); 589 590 IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length]; 591 592 for (int i = 0; i < enabledExtensions.Length; i++) 593 { 594 ppEnabledExtensions[i] = Marshal.StringToHGlobalAnsi(enabledExtensions[i]); 595 } 596 597 var deviceCreateInfo = new DeviceCreateInfo 598 { 599 SType = StructureType.DeviceCreateInfo, 600 PNext = pExtendedFeatures, 601 QueueCreateInfoCount = 1, 602 PQueueCreateInfos = &queueCreateInfo, 603 PpEnabledExtensionNames = (byte**)ppEnabledExtensions, 604 EnabledExtensionCount = (uint)enabledExtensions.Length, 605 PEnabledFeatures = &features, 606 }; 607 608 api.CreateDevice(physicalDevice.PhysicalDevice, in deviceCreateInfo, null, out var device).ThrowOnError(); 609 610 for (int i = 0; i < enabledExtensions.Length; i++) 611 { 612 Marshal.FreeHGlobal(ppEnabledExtensions[i]); 613 } 614 615 return device; 616 } 617 } 618 }