/ src / Ryujinx.Graphics.Vulkan / FormatCapabilities.cs
FormatCapabilities.cs
  1  using Ryujinx.Common.Logging;
  2  using Ryujinx.Graphics.GAL;
  3  using Silk.NET.Vulkan;
  4  using System;
  5  using Format = Ryujinx.Graphics.GAL.Format;
  6  using VkFormat = Silk.NET.Vulkan.Format;
  7  
  8  namespace Ryujinx.Graphics.Vulkan
  9  {
 10      class FormatCapabilities
 11      {
 12          private static readonly GAL.Format[] _scaledFormats = {
 13              GAL.Format.R8Uscaled,
 14              GAL.Format.R8Sscaled,
 15              GAL.Format.R16Uscaled,
 16              GAL.Format.R16Sscaled,
 17              GAL.Format.R8G8Uscaled,
 18              GAL.Format.R8G8Sscaled,
 19              GAL.Format.R16G16Uscaled,
 20              GAL.Format.R16G16Sscaled,
 21              GAL.Format.R8G8B8Uscaled,
 22              GAL.Format.R8G8B8Sscaled,
 23              GAL.Format.R16G16B16Uscaled,
 24              GAL.Format.R16G16B16Sscaled,
 25              GAL.Format.R8G8B8A8Uscaled,
 26              GAL.Format.R8G8B8A8Sscaled,
 27              GAL.Format.R16G16B16A16Uscaled,
 28              GAL.Format.R16G16B16A16Sscaled,
 29              GAL.Format.R10G10B10A2Uscaled,
 30              GAL.Format.R10G10B10A2Sscaled,
 31          };
 32  
 33          private static readonly GAL.Format[] _intFormats = {
 34              GAL.Format.R8Uint,
 35              GAL.Format.R8Sint,
 36              GAL.Format.R16Uint,
 37              GAL.Format.R16Sint,
 38              GAL.Format.R8G8Uint,
 39              GAL.Format.R8G8Sint,
 40              GAL.Format.R16G16Uint,
 41              GAL.Format.R16G16Sint,
 42              GAL.Format.R8G8B8Uint,
 43              GAL.Format.R8G8B8Sint,
 44              GAL.Format.R16G16B16Uint,
 45              GAL.Format.R16G16B16Sint,
 46              GAL.Format.R8G8B8A8Uint,
 47              GAL.Format.R8G8B8A8Sint,
 48              GAL.Format.R16G16B16A16Uint,
 49              GAL.Format.R16G16B16A16Sint,
 50              GAL.Format.R10G10B10A2Uint,
 51              GAL.Format.R10G10B10A2Sint,
 52          };
 53  
 54          private readonly FormatFeatureFlags[] _bufferTable;
 55          private readonly FormatFeatureFlags[] _optimalTable;
 56  
 57          private readonly Vk _api;
 58          private readonly PhysicalDevice _physicalDevice;
 59  
 60          public FormatCapabilities(Vk api, PhysicalDevice physicalDevice)
 61          {
 62              _api = api;
 63              _physicalDevice = physicalDevice;
 64  
 65              int totalFormats = Enum.GetNames(typeof(Format)).Length;
 66  
 67              _bufferTable = new FormatFeatureFlags[totalFormats];
 68              _optimalTable = new FormatFeatureFlags[totalFormats];
 69          }
 70  
 71          public bool BufferFormatsSupport(FormatFeatureFlags flags, params Format[] formats)
 72          {
 73              foreach (Format format in formats)
 74              {
 75                  if (!BufferFormatSupports(flags, format))
 76                  {
 77                      return false;
 78                  }
 79              }
 80  
 81              return true;
 82          }
 83  
 84          public bool OptimalFormatsSupport(FormatFeatureFlags flags, params Format[] formats)
 85          {
 86              foreach (Format format in formats)
 87              {
 88                  if (!OptimalFormatSupports(flags, format))
 89                  {
 90                      return false;
 91                  }
 92              }
 93  
 94              return true;
 95          }
 96  
 97          public bool BufferFormatSupports(FormatFeatureFlags flags, Format format)
 98          {
 99              var formatFeatureFlags = _bufferTable[(int)format];
100  
101              if (formatFeatureFlags == 0)
102              {
103                  _api.GetPhysicalDeviceFormatProperties(_physicalDevice, FormatTable.GetFormat(format), out var fp);
104                  formatFeatureFlags = fp.BufferFeatures;
105                  _bufferTable[(int)format] = formatFeatureFlags;
106              }
107  
108              return (formatFeatureFlags & flags) == flags;
109          }
110  
111          public bool SupportsScaledVertexFormats()
112          {
113              // We want to check is all scaled formats are supported,
114              // but if the integer variant is not supported either,
115              // then the format is likely not supported at all,
116              // we ignore formats that are entirely unsupported here.
117  
118              for (int i = 0; i < _scaledFormats.Length; i++)
119              {
120                  if (!BufferFormatSupports(FormatFeatureFlags.VertexBufferBit, _scaledFormats[i]) &&
121                      BufferFormatSupports(FormatFeatureFlags.VertexBufferBit, _intFormats[i]))
122                  {
123                      return false;
124                  }
125              }
126  
127              return true;
128          }
129  
130          public bool BufferFormatSupports(FormatFeatureFlags flags, VkFormat format)
131          {
132              _api.GetPhysicalDeviceFormatProperties(_physicalDevice, format, out var fp);
133  
134              return (fp.BufferFeatures & flags) == flags;
135          }
136  
137          public bool OptimalFormatSupports(FormatFeatureFlags flags, Format format)
138          {
139              var formatFeatureFlags = _optimalTable[(int)format];
140  
141              if (formatFeatureFlags == 0)
142              {
143                  _api.GetPhysicalDeviceFormatProperties(_physicalDevice, FormatTable.GetFormat(format), out var fp);
144                  formatFeatureFlags = fp.OptimalTilingFeatures;
145                  _optimalTable[(int)format] = formatFeatureFlags;
146              }
147  
148              return (formatFeatureFlags & flags) == flags;
149          }
150  
151          public VkFormat ConvertToVkFormat(Format srcFormat)
152          {
153              var format = FormatTable.GetFormat(srcFormat);
154  
155              var requiredFeatures = FormatFeatureFlags.SampledImageBit |
156                                     FormatFeatureFlags.TransferSrcBit |
157                                     FormatFeatureFlags.TransferDstBit;
158  
159              if (srcFormat.IsDepthOrStencil())
160              {
161                  requiredFeatures |= FormatFeatureFlags.DepthStencilAttachmentBit;
162              }
163              else if (srcFormat.IsRtColorCompatible())
164              {
165                  requiredFeatures |= FormatFeatureFlags.ColorAttachmentBit;
166              }
167  
168              if (srcFormat.IsImageCompatible())
169              {
170                  requiredFeatures |= FormatFeatureFlags.StorageImageBit;
171              }
172  
173              if (!OptimalFormatSupports(requiredFeatures, srcFormat) || (IsD24S8(srcFormat) && VulkanConfiguration.ForceD24S8Unsupported))
174              {
175                  // The format is not supported. Can we convert it to a higher precision format?
176                  if (IsD24S8(srcFormat))
177                  {
178                      format = VkFormat.D32SfloatS8Uint;
179                  }
180                  else if (srcFormat == Format.R4G4B4A4Unorm)
181                  {
182                      format = VkFormat.R4G4B4A4UnormPack16;
183                  }
184                  else
185                  {
186                      Logger.Error?.Print(LogClass.Gpu, $"Format {srcFormat} is not supported by the host.");
187                  }
188              }
189  
190              return format;
191          }
192  
193          public VkFormat ConvertToVertexVkFormat(Format srcFormat)
194          {
195              var format = FormatTable.GetFormat(srcFormat);
196  
197              if (!BufferFormatSupports(FormatFeatureFlags.VertexBufferBit, srcFormat) ||
198                  (IsRGB16IntFloat(srcFormat) && VulkanConfiguration.ForceRGB16IntFloatUnsupported))
199              {
200                  // The format is not supported. Can we convert it to an alternative format?
201                  switch (srcFormat)
202                  {
203                      case Format.R16G16B16Float:
204                          format = VkFormat.R16G16B16A16Sfloat;
205                          break;
206                      case Format.R16G16B16Sint:
207                          format = VkFormat.R16G16B16A16Sint;
208                          break;
209                      case Format.R16G16B16Uint:
210                          format = VkFormat.R16G16B16A16Uint;
211                          break;
212                      default:
213                          Logger.Error?.Print(LogClass.Gpu, $"Format {srcFormat} is not supported by the host.");
214                          break;
215                  }
216              }
217  
218              return format;
219          }
220  
221          public static bool IsD24S8(Format format)
222          {
223              return format == Format.D24UnormS8Uint || format == Format.S8UintD24Unorm || format == Format.X8UintD24Unorm;
224          }
225  
226          private static bool IsRGB16IntFloat(Format format)
227          {
228              return format == Format.R16G16B16Float ||
229                     format == Format.R16G16B16Sint ||
230                     format == Format.R16G16B16Uint;
231          }
232      }
233  }