From dca5b14493e730960ed5cd67906278ecea969b3a Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
Date: Fri, 13 Jan 2023 02:09:48 -0300
Subject: [PATCH] Relax Vulkan requirements (#4228)

---
 Ryujinx.Graphics.Vulkan/BufferManager.cs      | 22 ++++++-
 Ryujinx.Graphics.Vulkan/FramebufferParams.cs  |  3 +-
 Ryujinx.Graphics.Vulkan/MemoryAllocator.cs    | 33 ++++++++--
 Ryujinx.Graphics.Vulkan/PipelineBase.cs       |  3 +-
 Ryujinx.Graphics.Vulkan/Vendor.cs             |  3 +
 .../VulkanInitialization.cs                   | 66 +++++++++++++------
 Ryujinx.Graphics.Vulkan/VulkanRenderer.cs     |  6 +-
 Ryujinx.Graphics.Vulkan/Window.cs             | 22 +++++--
 8 files changed, 121 insertions(+), 37 deletions(-)

diff --git a/Ryujinx.Graphics.Vulkan/BufferManager.cs b/Ryujinx.Graphics.Vulkan/BufferManager.cs
index f32403712..9c50e6ff3 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 751ef5ebf..84ac0b051 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);
 
@@ -67,7 +67,6 @@ namespace Ryujinx.Graphics.Vulkan
             AttachmentSamples = new uint[count];
             AttachmentFormats = new VkFormat[count];
             AttachmentIndices = new int[count];
-            MaxColorAttachmentIndex = colors.Length - 1;
 
             uint width = uint.MaxValue;
             uint height = uint.MaxValue;
diff --git a/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs b/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs
index eea4e60b3..83c0a3243 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 1c0c836bb..c4a9f2df5 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;
             }
diff --git a/Ryujinx.Graphics.Vulkan/Vendor.cs b/Ryujinx.Graphics.Vulkan/Vendor.cs
index 087d6e9dc..5e0290c0a 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 fe9462aa3..d9dcc0b25 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,41 +427,48 @@ 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,
+                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,
@@ -465,7 +491,7 @@ namespace Ryujinx.Graphics.Vulkan
             {
                 SType = StructureType.PhysicalDeviceVulkan11Features,
                 PNext = pExtendedFeatures,
-                ShaderDrawParameters = true
+                ShaderDrawParameters = supportedFeaturesVk11.ShaderDrawParameters
             };
 
             pExtendedFeatures = &featuresVk11;
@@ -526,8 +552,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 f1922efed..029ad4b96 100644
--- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
+++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
@@ -590,7 +590,11 @@ namespace Ryujinx.Graphics.Vulkan
 
             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;
+            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 d37dd7e96..27ebb0de6 100644
--- a/Ryujinx.Graphics.Vulkan/Window.cs
+++ b/Ryujinx.Graphics.Vulkan/Window.cs
@@ -113,7 +113,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,
                 OldSwapchain = oldSwapchain
@@ -182,6 +182,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))
@@ -192,10 +208,6 @@ namespace Ryujinx.Graphics.Vulkan
             {
                 return PresentModeKHR.MailboxKhr;
             }
-            else if (availablePresentModes.Contains(PresentModeKHR.FifoKhr))
-            {
-               return PresentModeKHR.FifoKhr;
-            }
             else
             {
                 return PresentModeKHR.FifoKhr;