aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs')
-rw-r--r--src/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs164
1 files changed, 164 insertions, 0 deletions
diff --git a/src/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs b/src/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs
new file mode 100644
index 00000000..7019dfd9
--- /dev/null
+++ b/src/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs
@@ -0,0 +1,164 @@
+using Ryujinx.Common.Logging;
+using Ryujinx.Graphics.GAL;
+using Silk.NET.Vulkan;
+using System;
+using VkFormat = Silk.NET.Vulkan.Format;
+
+namespace Ryujinx.Graphics.Vulkan
+{
+ class FormatCapabilities
+ {
+ private readonly FormatFeatureFlags[] _bufferTable;
+ private readonly FormatFeatureFlags[] _optimalTable;
+
+ private readonly Vk _api;
+ private readonly PhysicalDevice _physicalDevice;
+
+ public FormatCapabilities(Vk api, PhysicalDevice physicalDevice)
+ {
+ _api = api;
+ _physicalDevice = physicalDevice;
+
+ int totalFormats = Enum.GetNames(typeof(GAL.Format)).Length;
+
+ _bufferTable = new FormatFeatureFlags[totalFormats];
+ _optimalTable = new FormatFeatureFlags[totalFormats];
+ }
+
+ public bool BufferFormatsSupport(FormatFeatureFlags flags, params GAL.Format[] formats)
+ {
+ foreach (GAL.Format format in formats)
+ {
+ if (!BufferFormatSupports(flags, format))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public bool OptimalFormatsSupport(FormatFeatureFlags flags, params GAL.Format[] formats)
+ {
+ foreach (GAL.Format format in formats)
+ {
+ if (!OptimalFormatSupports(flags, format))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public bool BufferFormatSupports(FormatFeatureFlags flags, GAL.Format format)
+ {
+ var formatFeatureFlags = _bufferTable[(int)format];
+
+ if (formatFeatureFlags == 0)
+ {
+ _api.GetPhysicalDeviceFormatProperties(_physicalDevice, FormatTable.GetFormat(format), out var fp);
+ formatFeatureFlags = fp.BufferFeatures;
+ _bufferTable[(int)format] = formatFeatureFlags;
+ }
+
+ return (formatFeatureFlags & flags) == flags;
+ }
+
+ public bool OptimalFormatSupports(FormatFeatureFlags flags, GAL.Format format)
+ {
+ var formatFeatureFlags = _optimalTable[(int)format];
+
+ if (formatFeatureFlags == 0)
+ {
+ _api.GetPhysicalDeviceFormatProperties(_physicalDevice, FormatTable.GetFormat(format), out var fp);
+ formatFeatureFlags = fp.OptimalTilingFeatures;
+ _optimalTable[(int)format] = formatFeatureFlags;
+ }
+
+ return (formatFeatureFlags & flags) == flags;
+ }
+
+ public VkFormat ConvertToVkFormat(GAL.Format srcFormat)
+ {
+ var format = FormatTable.GetFormat(srcFormat);
+
+ var requiredFeatures = FormatFeatureFlags.SampledImageBit |
+ FormatFeatureFlags.TransferSrcBit |
+ FormatFeatureFlags.TransferDstBit;
+
+ if (srcFormat.IsDepthOrStencil())
+ {
+ requiredFeatures |= FormatFeatureFlags.DepthStencilAttachmentBit;
+ }
+ else if (srcFormat.IsRtColorCompatible())
+ {
+ requiredFeatures |= FormatFeatureFlags.ColorAttachmentBit;
+ }
+
+ if (srcFormat.IsImageCompatible())
+ {
+ requiredFeatures |= FormatFeatureFlags.StorageImageBit;
+ }
+
+ if (!OptimalFormatSupports(requiredFeatures, srcFormat) || (IsD24S8(srcFormat) && VulkanConfiguration.ForceD24S8Unsupported))
+ {
+ // The format is not supported. Can we convert it to a higher precision format?
+ if (IsD24S8(srcFormat))
+ {
+ format = VkFormat.D32SfloatS8Uint;
+ }
+ else if (srcFormat == GAL.Format.R4G4B4A4Unorm)
+ {
+ format = VkFormat.R4G4B4A4UnormPack16;
+ }
+ else
+ {
+ Logger.Error?.Print(LogClass.Gpu, $"Format {srcFormat} is not supported by the host.");
+ }
+ }
+
+ return format;
+ }
+
+ public VkFormat ConvertToVertexVkFormat(GAL.Format srcFormat)
+ {
+ var format = FormatTable.GetFormat(srcFormat);
+
+ if (!BufferFormatSupports(FormatFeatureFlags.VertexBufferBit, srcFormat) ||
+ (IsRGB16IntFloat(srcFormat) && VulkanConfiguration.ForceRGB16IntFloatUnsupported))
+ {
+ // The format is not supported. Can we convert it to an alternative format?
+ switch (srcFormat)
+ {
+ case GAL.Format.R16G16B16Float:
+ format = VkFormat.R16G16B16A16Sfloat;
+ break;
+ case GAL.Format.R16G16B16Sint:
+ format = VkFormat.R16G16B16A16Sint;
+ break;
+ case GAL.Format.R16G16B16Uint:
+ format = VkFormat.R16G16B16A16Uint;
+ break;
+ default:
+ Logger.Error?.Print(LogClass.Gpu, $"Format {srcFormat} is not supported by the host.");
+ break;
+ }
+ }
+
+ return format;
+ }
+
+ public static bool IsD24S8(GAL.Format format)
+ {
+ return format == GAL.Format.D24UnormS8Uint || format == GAL.Format.S8UintD24Unorm;
+ }
+
+ private static bool IsRGB16IntFloat(GAL.Format format)
+ {
+ return format == GAL.Format.R16G16B16Float ||
+ format == GAL.Format.R16G16B16Sint ||
+ format == GAL.Format.R16G16B16Uint;
+ }
+ }
+}