From 296c4a3d012b1203b1b7e58cdc34c334159e1599 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Thu, 26 Jan 2023 18:34:35 -0300 Subject: [PATCH] Relax Vulkan requirements (#4282) * Relax Vulkan requirements * Fix MaxColorAttachmentIndex * Fix ColorBlendAttachmentStateCount value mismatch for background pipelines * Change query capability check to check for pipeline statistics query rather than geometry shader support --- Ryujinx.Graphics.Vulkan/BufferManager.cs | 22 ++++++- Ryujinx.Graphics.Vulkan/FramebufferParams.cs | 5 +- .../HardwareCapabilities.cs | 5 +- Ryujinx.Graphics.Vulkan/MemoryAllocator.cs | 33 ++++++++-- Ryujinx.Graphics.Vulkan/PipelineBase.cs | 5 +- Ryujinx.Graphics.Vulkan/PipelineConverter.cs | 18 ++--- .../Queries/BufferedQuery.cs | 2 +- Ryujinx.Graphics.Vulkan/Vendor.cs | 3 + .../VulkanInitialization.cs | 66 +++++++++++++------ Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 11 +++- Ryujinx.Graphics.Vulkan/Window.cs | 22 +++++-- 11 files changed, 139 insertions(+), 53 deletions(-) diff --git a/Ryujinx.Graphics.Vulkan/BufferManager.cs b/Ryujinx.Graphics.Vulkan/BufferManager.cs index f324037123..9c50e6ff38 100644 --- a/Ryujinx.Graphics.Vulkan/BufferManager.cs +++ b/Ryujinx.Graphics.Vulkan/BufferManager.cs @@ -14,6 +14,12 @@ namespace Ryujinx.Graphics.Vulkan MemoryPropertyFlags.HostCoherentBit | MemoryPropertyFlags.HostCachedBit; + // Some drivers don't expose a "HostCached" memory type, + // so we need those alternative flags for the allocation to succeed there. + private const MemoryPropertyFlags DefaultBufferMemoryAltFlags = + MemoryPropertyFlags.HostVisibleBit | + MemoryPropertyFlags.HostCoherentBit; + private const MemoryPropertyFlags DeviceLocalBufferMemoryFlags = MemoryPropertyFlags.DeviceLocalBit; @@ -94,9 +100,21 @@ namespace Ryujinx.Graphics.Vulkan gd.Api.CreateBuffer(_device, in bufferCreateInfo, null, out var buffer).ThrowOnError(); gd.Api.GetBufferMemoryRequirements(_device, buffer, out var requirements); - var allocateFlags = deviceLocal ? DeviceLocalBufferMemoryFlags : DefaultBufferMemoryFlags; + MemoryPropertyFlags allocateFlags; + MemoryPropertyFlags allocateFlagsAlt; - var allocation = gd.MemoryAllocator.AllocateDeviceMemory(_physicalDevice, requirements, allocateFlags); + if (deviceLocal) + { + allocateFlags = DeviceLocalBufferMemoryFlags; + allocateFlagsAlt = DeviceLocalBufferMemoryFlags; + } + else + { + allocateFlags = DefaultBufferMemoryFlags; + allocateFlagsAlt = DefaultBufferMemoryAltFlags; + } + + var allocation = gd.MemoryAllocator.AllocateDeviceMemory(_physicalDevice, requirements, allocateFlags, allocateFlagsAlt); if (allocation.Memory.Handle == 0UL) { diff --git a/Ryujinx.Graphics.Vulkan/FramebufferParams.cs b/Ryujinx.Graphics.Vulkan/FramebufferParams.cs index 751ef5ebf0..ff8d423b83 100644 --- a/Ryujinx.Graphics.Vulkan/FramebufferParams.cs +++ b/Ryujinx.Graphics.Vulkan/FramebufferParams.cs @@ -23,7 +23,7 @@ namespace Ryujinx.Graphics.Vulkan public int[] AttachmentIndices { get; } public int AttachmentsCount { get; } - public int MaxColorAttachmentIndex { get; } + public int MaxColorAttachmentIndex => AttachmentIndices.Length > 0 ? AttachmentIndices[AttachmentIndices.Length - 1] : -1; public bool HasDepthStencil { get; } public int ColorAttachmentsCount => AttachmentsCount - (HasDepthStencil ? 1 : 0); @@ -66,8 +66,7 @@ namespace Ryujinx.Graphics.Vulkan AttachmentSamples = new uint[count]; AttachmentFormats = new VkFormat[count]; - AttachmentIndices = new int[count]; - MaxColorAttachmentIndex = colors.Length - 1; + AttachmentIndices = new int[colorsCount]; uint width = uint.MaxValue; uint height = uint.MaxValue; diff --git a/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs b/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs index 8685d34421..376f396083 100644 --- a/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs +++ b/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs @@ -28,10 +28,11 @@ namespace Ryujinx.Graphics.Vulkan public readonly bool SupportsExtendedDynamicState; public readonly bool SupportsMultiView; public readonly bool SupportsNullDescriptors; - public readonly bool SupportsPreciseOcclusionQueries; public readonly bool SupportsPushDescriptors; public readonly bool SupportsTransformFeedback; public readonly bool SupportsTransformFeedbackQueries; + public readonly bool SupportsPreciseOcclusionQueries; + public readonly bool SupportsPipelineStatisticsQuery; public readonly bool SupportsGeometryShader; public readonly uint MinSubgroupSize; public readonly uint MaxSubgroupSize; @@ -55,6 +56,7 @@ namespace Ryujinx.Graphics.Vulkan bool supportsTransformFeedback, bool supportsTransformFeedbackQueries, bool supportsPreciseOcclusionQueries, + bool supportsPipelineStatisticsQuery, bool supportsGeometryShader, uint minSubgroupSize, uint maxSubgroupSize, @@ -77,6 +79,7 @@ namespace Ryujinx.Graphics.Vulkan SupportsTransformFeedback = supportsTransformFeedback; SupportsTransformFeedbackQueries = supportsTransformFeedbackQueries; SupportsPreciseOcclusionQueries = supportsPreciseOcclusionQueries; + SupportsPipelineStatisticsQuery = supportsPipelineStatisticsQuery; SupportsGeometryShader = supportsGeometryShader; MinSubgroupSize = minSubgroupSize; MaxSubgroupSize = maxSubgroupSize; diff --git a/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs b/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs index eea4e60b35..83c0a32430 100644 --- a/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs +++ b/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs @@ -27,7 +27,16 @@ namespace Ryujinx.Graphics.Vulkan MemoryRequirements requirements, MemoryPropertyFlags flags = 0) { - int memoryTypeIndex = FindSuitableMemoryTypeIndex(_api, physicalDevice, requirements.MemoryTypeBits, flags); + return AllocateDeviceMemory(physicalDevice, requirements, flags, flags); + } + + public MemoryAllocation AllocateDeviceMemory( + PhysicalDevice physicalDevice, + MemoryRequirements requirements, + MemoryPropertyFlags flags, + MemoryPropertyFlags alternativeFlags) + { + int memoryTypeIndex = FindSuitableMemoryTypeIndex(_api, physicalDevice, requirements.MemoryTypeBits, flags, alternativeFlags); if (memoryTypeIndex < 0) { return default; @@ -56,21 +65,35 @@ namespace Ryujinx.Graphics.Vulkan return newBl.Allocate(size, alignment, map); } - private static int FindSuitableMemoryTypeIndex(Vk api, PhysicalDevice physicalDevice, uint memoryTypeBits, MemoryPropertyFlags flags) + private static int FindSuitableMemoryTypeIndex( + Vk api, + PhysicalDevice physicalDevice, + uint memoryTypeBits, + MemoryPropertyFlags flags, + MemoryPropertyFlags alternativeFlags) { + int bestCandidateIndex = -1; + api.GetPhysicalDeviceMemoryProperties(physicalDevice, out var properties); for (int i = 0; i < properties.MemoryTypeCount; i++) { var type = properties.MemoryTypes[i]; - if ((memoryTypeBits & (1 << i)) != 0 && type.PropertyFlags.HasFlag(flags)) + if ((memoryTypeBits & (1 << i)) != 0) { - return i; + if (type.PropertyFlags.HasFlag(flags)) + { + return i; + } + else if (type.PropertyFlags.HasFlag(alternativeFlags)) + { + bestCandidateIndex = i; + } } } - return -1; + return bestCandidateIndex; } public void Dispose() diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs index c53acfe104..43dccf86ef 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -1344,8 +1344,7 @@ namespace Ryujinx.Graphics.Vulkan var dstAttachmentFormats = _newState.Internal.AttachmentFormats.AsSpan(); FramebufferParams.AttachmentFormats.CopyTo(dstAttachmentFormats); - int maxAttachmentIndex = FramebufferParams.MaxColorAttachmentIndex + (FramebufferParams.HasDepthStencil ? 1 : 0); - for (int i = FramebufferParams.AttachmentFormats.Length; i <= maxAttachmentIndex; i++) + for (int i = FramebufferParams.AttachmentFormats.Length; i < dstAttachmentFormats.Length; i++) { dstAttachmentFormats[i] = 0; } @@ -1376,8 +1375,6 @@ namespace Ryujinx.Graphics.Vulkan for (int i = 0; i < FramebufferParams.AttachmentsCount; i++) { - int bindIndex = FramebufferParams.AttachmentIndices[i]; - attachmentDescs[i] = new AttachmentDescription( 0, FramebufferParams.AttachmentFormats[i], diff --git a/Ryujinx.Graphics.Vulkan/PipelineConverter.cs b/Ryujinx.Graphics.Vulkan/PipelineConverter.cs index 3e85ec36a1..26d34e54e7 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineConverter.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineConverter.cs @@ -27,14 +27,11 @@ namespace Ryujinx.Graphics.Vulkan int attachmentCount = 0; int colorCount = 0; - int maxColorAttachmentIndex = 0; for (int i = 0; i < state.AttachmentEnable.Length; i++) { if (state.AttachmentEnable[i]) { - maxColorAttachmentIndex = i; - attachmentFormats[attachmentCount] = gd.FormatCapabilities.ConvertToVkFormat(state.AttachmentFormats[i]); attachmentIndices[attachmentCount++] = i; @@ -270,7 +267,7 @@ namespace Ryujinx.Graphics.Vulkan // NOTE: Viewports, Scissors are dynamic. - for (int i = 0; i < 8; i++) + for (int i = 0; i < Constants.MaxRenderTargets; i++) { var blend = state.BlendDescriptors[i]; @@ -293,21 +290,24 @@ namespace Ryujinx.Graphics.Vulkan } } - int maxAttachmentIndex = 0; - for (int i = 0; i < 8; i++) + int attachmentCount = 0; + int maxColorAttachmentIndex = -1; + + for (int i = 0; i < Constants.MaxRenderTargets; i++) { if (state.AttachmentEnable[i]) { - pipeline.Internal.AttachmentFormats[maxAttachmentIndex++] = gd.FormatCapabilities.ConvertToVkFormat(state.AttachmentFormats[i]); + pipeline.Internal.AttachmentFormats[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.AttachmentFormats[i]); + maxColorAttachmentIndex = i; } } if (state.DepthStencilEnable) { - pipeline.Internal.AttachmentFormats[maxAttachmentIndex++] = gd.FormatCapabilities.ConvertToVkFormat(state.DepthStencilFormat); + pipeline.Internal.AttachmentFormats[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.DepthStencilFormat); } - pipeline.ColorBlendAttachmentStateCount = 8; + pipeline.ColorBlendAttachmentStateCount = (uint)(maxColorAttachmentIndex + 1); pipeline.VertexAttributeDescriptionsCount = (uint)Math.Min(Constants.MaxVertexAttributes, state.VertexAttribCount); return pipeline; diff --git a/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs b/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs index a1a5eb2794..abb43eb565 100644 --- a/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs +++ b/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs @@ -67,8 +67,8 @@ namespace Ryujinx.Graphics.Vulkan.Queries return type switch { CounterType.SamplesPassed => true, + CounterType.PrimitivesGenerated => gd.Capabilities.SupportsPipelineStatisticsQuery, CounterType.TransformFeedbackPrimitivesWritten => gd.Capabilities.SupportsTransformFeedbackQueries, - CounterType.PrimitivesGenerated => gd.Capabilities.SupportsGeometryShader, _ => false }; } diff --git a/Ryujinx.Graphics.Vulkan/Vendor.cs b/Ryujinx.Graphics.Vulkan/Vendor.cs index 087d6e9dca..5e0290c0a6 100644 --- a/Ryujinx.Graphics.Vulkan/Vendor.cs +++ b/Ryujinx.Graphics.Vulkan/Vendor.cs @@ -9,6 +9,7 @@ namespace Ryujinx.Graphics.Vulkan Intel, Nvidia, ARM, + Broadcom, Qualcomm, Apple, Unknown @@ -28,6 +29,7 @@ namespace Ryujinx.Graphics.Vulkan 0x106B => Vendor.Apple, 0x10DE => Vendor.Nvidia, 0x13B5 => Vendor.ARM, + 0x14E4 => Vendor.Broadcom, 0x8086 => Vendor.Intel, 0x5143 => Vendor.Qualcomm, _ => Vendor.Unknown @@ -43,6 +45,7 @@ namespace Ryujinx.Graphics.Vulkan 0x106B => "Apple", 0x10DE => "NVIDIA", 0x13B5 => "ARM", + 0x14E4 => "Broadcom", 0x1AE0 => "Google", 0x5143 => "Qualcomm", 0x8086 => "Intel", diff --git a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs index 00baad1036..f38fc8ab83 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs @@ -162,7 +162,6 @@ namespace Ryujinx.Graphics.Vulkan if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt)) { Logger.Error?.Print(LogClass.Gpu, msg); - //throw new Exception(msg); } else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.WarningBitExt)) { @@ -379,14 +378,34 @@ namespace Ryujinx.Graphics.Vulkan SType = StructureType.PhysicalDeviceFeatures2 }; - PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColorSupported = new PhysicalDeviceCustomBorderColorFeaturesEXT() + PhysicalDeviceVulkan11Features supportedFeaturesVk11 = new PhysicalDeviceVulkan11Features() { - SType = StructureType.PhysicalDeviceCustomBorderColorFeaturesExt + SType = StructureType.PhysicalDeviceVulkan11Features, + PNext = features2.PNext + }; + + features2.PNext = &supportedFeaturesVk11; + + PhysicalDeviceCustomBorderColorFeaturesEXT supportedFeaturesCustomBorderColor = new PhysicalDeviceCustomBorderColorFeaturesEXT() + { + SType = StructureType.PhysicalDeviceCustomBorderColorFeaturesExt, + PNext = features2.PNext }; if (supportedExtensions.Contains("VK_EXT_custom_border_color")) { - features2.PNext = &featuresCustomBorderColorSupported; + features2.PNext = &supportedFeaturesCustomBorderColor; + } + + PhysicalDeviceTransformFeedbackFeaturesEXT supportedFeaturesTransformFeedback = new PhysicalDeviceTransformFeedbackFeaturesEXT() + { + SType = StructureType.PhysicalDeviceTransformFeedbackFeaturesExt, + PNext = features2.PNext + }; + + if (supportedExtensions.Contains(ExtTransformFeedback.ExtensionName)) + { + features2.PNext = &supportedFeaturesTransformFeedback; } PhysicalDeviceRobustness2FeaturesEXT supportedFeaturesRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT() @@ -408,42 +427,49 @@ namespace Ryujinx.Graphics.Vulkan var features = new PhysicalDeviceFeatures() { DepthBiasClamp = true, - DepthClamp = true, - DualSrcBlend = true, + DepthClamp = supportedFeatures.DepthClamp, + DualSrcBlend = supportedFeatures.DualSrcBlend, FragmentStoresAndAtomics = true, GeometryShader = supportedFeatures.GeometryShader, ImageCubeArray = true, IndependentBlend = true, LogicOp = supportedFeatures.LogicOp, - MultiViewport = true, OcclusionQueryPrecise = supportedFeatures.OcclusionQueryPrecise, + MultiViewport = supportedFeatures.MultiViewport, PipelineStatisticsQuery = supportedFeatures.PipelineStatisticsQuery, SamplerAnisotropy = true, ShaderClipDistance = true, ShaderFloat64 = supportedFeatures.ShaderFloat64, - ShaderImageGatherExtended = true, + ShaderImageGatherExtended = supportedFeatures.ShaderImageGatherExtended, ShaderStorageImageMultisample = supportedFeatures.ShaderStorageImageMultisample, // ShaderStorageImageReadWithoutFormat = true, // ShaderStorageImageWriteWithoutFormat = true, - TessellationShader = true, + TessellationShader = supportedFeatures.TessellationShader, VertexPipelineStoresAndAtomics = true, RobustBufferAccess = useRobustBufferAccess }; void* pExtendedFeatures = null; - var featuresTransformFeedback = new PhysicalDeviceTransformFeedbackFeaturesEXT() - { - SType = StructureType.PhysicalDeviceTransformFeedbackFeaturesExt, - PNext = pExtendedFeatures, - TransformFeedback = true - }; + PhysicalDeviceTransformFeedbackFeaturesEXT featuresTransformFeedback; - pExtendedFeatures = &featuresTransformFeedback; + if (supportedExtensions.Contains(ExtTransformFeedback.ExtensionName)) + { + featuresTransformFeedback = new PhysicalDeviceTransformFeedbackFeaturesEXT() + { + SType = StructureType.PhysicalDeviceTransformFeedbackFeaturesExt, + PNext = pExtendedFeatures, + TransformFeedback = supportedFeaturesTransformFeedback.TransformFeedback + }; + + pExtendedFeatures = &featuresTransformFeedback; + } + + PhysicalDeviceRobustness2FeaturesEXT featuresRobustness2; if (supportedExtensions.Contains("VK_EXT_robustness2")) { - var featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT() + featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT() { SType = StructureType.PhysicalDeviceRobustness2FeaturesExt, PNext = pExtendedFeatures, @@ -466,7 +492,7 @@ namespace Ryujinx.Graphics.Vulkan { SType = StructureType.PhysicalDeviceVulkan11Features, PNext = pExtendedFeatures, - ShaderDrawParameters = true + ShaderDrawParameters = supportedFeaturesVk11.ShaderDrawParameters }; pExtendedFeatures = &featuresVk11; @@ -527,8 +553,8 @@ namespace Ryujinx.Graphics.Vulkan PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColor; if (supportedExtensions.Contains("VK_EXT_custom_border_color") && - featuresCustomBorderColorSupported.CustomBorderColors && - featuresCustomBorderColorSupported.CustomBorderColorWithoutFormat) + supportedFeaturesCustomBorderColor.CustomBorderColors && + supportedFeaturesCustomBorderColor.CustomBorderColorWithoutFormat) { featuresCustomBorderColor = new PhysicalDeviceCustomBorderColorFeaturesEXT() { diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index f53f794009..085a7e930b 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -271,6 +271,7 @@ namespace Ryujinx.Graphics.Vulkan supportsTransformFeedback, propertiesTransformFeedback.TransformFeedbackQueries, features2.Features.OcclusionQueryPrecise, + supportedFeatures.PipelineStatisticsQuery, supportedFeatures.GeometryShader, propertiesSubgroupSizeControl.MinSubgroupSize, propertiesSubgroupSizeControl.MaxSubgroupSize, @@ -589,9 +590,13 @@ namespace Ryujinx.Graphics.Vulkan Vendor = VendorUtils.FromId(properties.VendorID); - IsAmdWindows = Vendor == Vendor.Amd && RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - IsIntelWindows = Vendor == Vendor.Intel && RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - IsTBDR = IsMoltenVk || Vendor == Vendor.Qualcomm || Vendor == Vendor.ARM || Vendor == Vendor.ImgTec; + IsAmdWindows = Vendor == Vendor.Amd && OperatingSystem.IsWindows(); + IsIntelWindows = Vendor == Vendor.Intel && OperatingSystem.IsWindows(); + IsTBDR = IsMoltenVk || + Vendor == Vendor.Qualcomm || + Vendor == Vendor.ARM || + Vendor == Vendor.Broadcom || + Vendor == Vendor.ImgTec; GpuVendor = vendorName; GpuRenderer = Marshal.PtrToStringAnsi((IntPtr)properties.DeviceName); diff --git a/Ryujinx.Graphics.Vulkan/Window.cs b/Ryujinx.Graphics.Vulkan/Window.cs index 7412ad90d9..9f731e0e6e 100644 --- a/Ryujinx.Graphics.Vulkan/Window.cs +++ b/Ryujinx.Graphics.Vulkan/Window.cs @@ -120,7 +120,7 @@ namespace Ryujinx.Graphics.Vulkan ImageSharingMode = SharingMode.Exclusive, ImageArrayLayers = 1, PreTransform = capabilities.CurrentTransform, - CompositeAlpha = CompositeAlphaFlagsKHR.OpaqueBitKhr, + CompositeAlpha = ChooseCompositeAlpha(capabilities.SupportedCompositeAlpha), PresentMode = ChooseSwapPresentMode(presentModes, _vsyncEnabled), Clipped = true }; @@ -188,6 +188,22 @@ namespace Ryujinx.Graphics.Vulkan return availableFormats[0]; } + private static CompositeAlphaFlagsKHR ChooseCompositeAlpha(CompositeAlphaFlagsKHR supportedFlags) + { + if (supportedFlags.HasFlag(CompositeAlphaFlagsKHR.OpaqueBitKhr)) + { + return CompositeAlphaFlagsKHR.OpaqueBitKhr; + } + else if (supportedFlags.HasFlag(CompositeAlphaFlagsKHR.PreMultipliedBitKhr)) + { + return CompositeAlphaFlagsKHR.PreMultipliedBitKhr; + } + else + { + return CompositeAlphaFlagsKHR.InheritBitKhr; + } + } + private static PresentModeKHR ChooseSwapPresentMode(PresentModeKHR[] availablePresentModes, bool vsyncEnabled) { if (!vsyncEnabled && availablePresentModes.Contains(PresentModeKHR.ImmediateKhr)) @@ -198,10 +214,6 @@ namespace Ryujinx.Graphics.Vulkan { return PresentModeKHR.MailboxKhr; } - else if (availablePresentModes.Contains(PresentModeKHR.FifoKhr)) - { - return PresentModeKHR.FifoKhr; - } else { return PresentModeKHR.FifoKhr;