/ src / Ryujinx.Graphics.Gpu / Image / TextureCompatibility.cs
TextureCompatibility.cs
  1  using Ryujinx.Common;
  2  using Ryujinx.Graphics.GAL;
  3  using Ryujinx.Graphics.Texture;
  4  using System;
  5  using System.Diagnostics;
  6  using System.Numerics;
  7  
  8  namespace Ryujinx.Graphics.Gpu.Image
  9  {
 10      /// <summary>
 11      /// Texture format compatibility checks.
 12      /// </summary>
 13      static class TextureCompatibility
 14      {
 15          private enum FormatClass
 16          {
 17              Unclassified,
 18              Bc1Rgba,
 19              Bc2,
 20              Bc3,
 21              Bc4,
 22              Bc5,
 23              Bc6,
 24              Bc7,
 25              Etc2Rgb,
 26              Etc2Rgba,
 27              Astc4x4,
 28              Astc5x4,
 29              Astc5x5,
 30              Astc6x5,
 31              Astc6x6,
 32              Astc8x5,
 33              Astc8x6,
 34              Astc8x8,
 35              Astc10x5,
 36              Astc10x6,
 37              Astc10x8,
 38              Astc10x10,
 39              Astc12x10,
 40              Astc12x12,
 41          }
 42  
 43          /// <summary>
 44          /// Checks if a format is host incompatible.
 45          /// </summary>
 46          /// <remarks>
 47          /// Host incompatible formats can't be used directly, the texture data needs to be converted
 48          /// to a compatible format first.
 49          /// </remarks>
 50          /// <param name="info">Texture information</param>
 51          /// <param name="caps">Host GPU capabilities</param>
 52          /// <returns>True if the format is incompatible, false otherwise</returns>
 53          public static bool IsFormatHostIncompatible(TextureInfo info, Capabilities caps)
 54          {
 55              Format originalFormat = info.FormatInfo.Format;
 56              return ToHostCompatibleFormat(info, caps).Format != originalFormat;
 57          }
 58  
 59          /// <summary>
 60          /// Converts a incompatible format to a host compatible format, or return the format directly
 61          /// if it is already host compatible.
 62          /// </summary>
 63          /// <remarks>
 64          /// This can be used to convert a incompatible compressed format to the decompressor
 65          /// output format.
 66          /// </remarks>
 67          /// <param name="info">Texture information</param>
 68          /// <param name="caps">Host GPU capabilities</param>
 69          /// <returns>A host compatible format</returns>
 70          public static FormatInfo ToHostCompatibleFormat(TextureInfo info, Capabilities caps)
 71          {
 72              // The host API does not support those compressed formats.
 73              // We assume software decompression will be done for those textures,
 74              // and so we adjust the format here to match the decompressor output.
 75  
 76              if (!caps.SupportsAstcCompression)
 77              {
 78                  if (info.FormatInfo.Format.IsAstcUnorm())
 79                  {
 80                      return GraphicsConfig.EnableTextureRecompression
 81                          ? new FormatInfo(Format.Bc7Unorm, 4, 4, 16, 4)
 82                          : new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4);
 83                  }
 84                  else if (info.FormatInfo.Format.IsAstcSrgb())
 85                  {
 86                      return GraphicsConfig.EnableTextureRecompression
 87                          ? new FormatInfo(Format.Bc7Srgb, 4, 4, 16, 4)
 88                          : new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4, 4);
 89                  }
 90              }
 91  
 92              if (!HostSupportsBcFormat(info.FormatInfo.Format, info.Target, caps))
 93              {
 94                  switch (info.FormatInfo.Format)
 95                  {
 96                      case Format.Bc1RgbaSrgb:
 97                      case Format.Bc2Srgb:
 98                      case Format.Bc3Srgb:
 99                      case Format.Bc7Srgb:
100                          return new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4, 4);
101                      case Format.Bc1RgbaUnorm:
102                      case Format.Bc2Unorm:
103                      case Format.Bc3Unorm:
104                      case Format.Bc7Unorm:
105                          return new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4);
106                      case Format.Bc4Unorm:
107                          return new FormatInfo(Format.R8Unorm, 1, 1, 1, 1);
108                      case Format.Bc4Snorm:
109                          return new FormatInfo(Format.R8Snorm, 1, 1, 1, 1);
110                      case Format.Bc5Unorm:
111                          return new FormatInfo(Format.R8G8Unorm, 1, 1, 2, 2);
112                      case Format.Bc5Snorm:
113                          return new FormatInfo(Format.R8G8Snorm, 1, 1, 2, 2);
114                      case Format.Bc6HSfloat:
115                      case Format.Bc6HUfloat:
116                          return new FormatInfo(Format.R16G16B16A16Float, 1, 1, 8, 4);
117                  }
118              }
119  
120              if (!caps.SupportsEtc2Compression)
121              {
122                  switch (info.FormatInfo.Format)
123                  {
124                      case Format.Etc2RgbaSrgb:
125                      case Format.Etc2RgbPtaSrgb:
126                      case Format.Etc2RgbSrgb:
127                          return new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4, 4);
128                      case Format.Etc2RgbaUnorm:
129                      case Format.Etc2RgbPtaUnorm:
130                      case Format.Etc2RgbUnorm:
131                          return new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4);
132                  }
133              }
134  
135              if (!caps.SupportsR4G4Format && info.FormatInfo.Format == Format.R4G4Unorm)
136              {
137                  if (caps.SupportsR4G4B4A4Format)
138                  {
139                      return new FormatInfo(Format.R4G4B4A4Unorm, 1, 1, 2, 4);
140                  }
141                  else
142                  {
143                      return new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4);
144                  }
145              }
146  
147              if (info.FormatInfo.Format == Format.R4G4B4A4Unorm)
148              {
149                  if (!caps.SupportsR4G4B4A4Format)
150                  {
151                      return new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4);
152                  }
153              }
154              else if (!caps.Supports5BitComponentFormat && info.FormatInfo.Format.Is16BitPacked())
155              {
156                  return new FormatInfo(info.FormatInfo.Format.IsBgr() ? Format.B8G8R8A8Unorm : Format.R8G8B8A8Unorm, 1, 1, 4, 4);
157              }
158  
159              return info.FormatInfo;
160          }
161  
162          /// <summary>
163          /// Checks if the host API supports a given texture compression format of the BC family.
164          /// </summary>
165          /// <param name="format">BC format to be checked</param>
166          /// <param name="target">Target usage of the texture</param>
167          /// <param name="caps">Host GPU Capabilities</param>
168          /// <returns>True if the texture host supports the format with the given target usage, false otherwise</returns>
169          public static bool HostSupportsBcFormat(Format format, Target target, Capabilities caps)
170          {
171              bool not3DOr3DCompressionSupported = target != Target.Texture3D || caps.Supports3DTextureCompression;
172  
173              switch (format)
174              {
175                  case Format.Bc1RgbaSrgb:
176                  case Format.Bc1RgbaUnorm:
177                  case Format.Bc2Srgb:
178                  case Format.Bc2Unorm:
179                  case Format.Bc3Srgb:
180                  case Format.Bc3Unorm:
181                      return caps.SupportsBc123Compression && not3DOr3DCompressionSupported;
182                  case Format.Bc4Unorm:
183                  case Format.Bc4Snorm:
184                  case Format.Bc5Unorm:
185                  case Format.Bc5Snorm:
186                      return caps.SupportsBc45Compression && not3DOr3DCompressionSupported;
187                  case Format.Bc6HSfloat:
188                  case Format.Bc6HUfloat:
189                  case Format.Bc7Srgb:
190                  case Format.Bc7Unorm:
191                      return caps.SupportsBc67Compression && not3DOr3DCompressionSupported;
192              }
193  
194              return true;
195          }
196  
197          /// <summary>
198          /// Determines whether a texture can flush its data back to guest memory.
199          /// </summary>
200          /// <param name="info">Texture information</param>
201          /// <param name="caps">Host GPU Capabilities</param>
202          /// <returns>True if the texture can flush, false otherwise</returns>
203          public static bool CanTextureFlush(TextureInfo info, Capabilities caps)
204          {
205              if (IsFormatHostIncompatible(info, caps))
206              {
207                  return false; // Flushing this format is not supported, as it may have been converted to another host format.
208              }
209  
210              if (info.Target == Target.Texture2DMultisample ||
211                  info.Target == Target.Texture2DMultisampleArray)
212              {
213                  return false; // Flushing multisample textures is not supported, the host does not allow getting their data.
214              }
215  
216              return true;
217          }
218  
219          /// <summary>
220          /// Checks if the texture format matches with the specified texture information.
221          /// </summary>
222          /// <param name="lhs">Texture information to compare</param>
223          /// <param name="rhs">Texture information to compare with</param>
224          /// <param name="forSampler">Indicates that the texture will be used for shader sampling</param>
225          /// <param name="depthAlias">Indicates if aliasing between color and depth format should be allowed</param>
226          /// <returns>A value indicating how well the formats match</returns>
227          public static TextureMatchQuality FormatMatches(TextureInfo lhs, TextureInfo rhs, bool forSampler, bool depthAlias)
228          {
229              // D32F and R32F texture have the same representation internally,
230              // however the R32F format is used to sample from depth textures.
231              if (IsValidDepthAsColorAlias(lhs.FormatInfo.Format, rhs.FormatInfo.Format) && (forSampler || depthAlias))
232              {
233                  return TextureMatchQuality.FormatAlias;
234              }
235  
236              if (depthAlias)
237              {
238                  // The 2D engine does not support depth-stencil formats, so it will instead
239                  // use equivalent color formats. We must also consider them as compatible.
240                  if (lhs.FormatInfo.Format == Format.S8Uint && rhs.FormatInfo.Format == Format.R8Unorm)
241                  {
242                      return TextureMatchQuality.FormatAlias;
243                  }
244                  else if ((lhs.FormatInfo.Format == Format.D24UnormS8Uint ||
245                            lhs.FormatInfo.Format == Format.S8UintD24Unorm ||
246                            lhs.FormatInfo.Format == Format.X8UintD24Unorm) && rhs.FormatInfo.Format == Format.B8G8R8A8Unorm)
247                  {
248                      return TextureMatchQuality.FormatAlias;
249                  }
250                  else if (lhs.FormatInfo.Format == Format.D32FloatS8Uint && rhs.FormatInfo.Format == Format.R32G32Float)
251                  {
252                      return TextureMatchQuality.FormatAlias;
253                  }
254              }
255  
256              return lhs.FormatInfo.Format == rhs.FormatInfo.Format ? TextureMatchQuality.Perfect : TextureMatchQuality.NoMatch;
257          }
258  
259          /// <summary>
260          /// Checks if the texture layout specified matches with this texture layout.
261          /// The layout information is composed of the Stride for linear textures, or GOB block size
262          /// for block linear textures.
263          /// </summary>
264          /// <param name="lhs">Texture information to compare</param>
265          /// <param name="rhs">Texture information to compare with</param>
266          /// <returns>True if the layout matches, false otherwise</returns>
267          public static bool LayoutMatches(TextureInfo lhs, TextureInfo rhs)
268          {
269              if (lhs.IsLinear != rhs.IsLinear)
270              {
271                  return false;
272              }
273  
274              // For linear textures, gob block sizes are ignored.
275              // For block linear textures, the stride is ignored.
276              if (rhs.IsLinear)
277              {
278                  return lhs.Stride == rhs.Stride;
279              }
280              else
281              {
282                  return lhs.GobBlocksInY == rhs.GobBlocksInY &&
283                         lhs.GobBlocksInZ == rhs.GobBlocksInZ;
284              }
285          }
286  
287          /// <summary>
288          /// Obtain the minimum compatibility level of two provided view compatibility results.
289          /// </summary>
290          /// <param name="first">The first compatibility level</param>
291          /// <param name="second">The second compatibility level</param>
292          /// <returns>The minimum compatibility level of two provided view compatibility results</returns>
293          public static TextureViewCompatibility PropagateViewCompatibility(TextureViewCompatibility first, TextureViewCompatibility second)
294          {
295              return (TextureViewCompatibility)Math.Min((int)first, (int)second);
296          }
297  
298          /// <summary>
299          /// Checks if the sizes of two texture levels are copy compatible.
300          /// </summary>
301          /// <param name="lhs">Texture information of the texture view</param>
302          /// <param name="rhs">Texture information of the texture view to match against</param>
303          /// <param name="lhsLevel">Mipmap level of the texture view in relation to this texture</param>
304          /// <param name="rhsLevel">Mipmap level of the texture view in relation to the second texture</param>
305          /// <returns>True if both levels are view compatible</returns>
306          public static bool CopySizeMatches(TextureInfo lhs, TextureInfo rhs, int lhsLevel, int rhsLevel)
307          {
308              Size size = GetAlignedSize(lhs, lhsLevel);
309  
310              Size otherSize = GetAlignedSize(rhs, rhsLevel);
311  
312              if (size.Width == otherSize.Width && size.Height == otherSize.Height)
313              {
314                  return true;
315              }
316              else if (lhs.IsLinear && rhs.IsLinear)
317              {
318                  // Copy between linear textures with matching stride.
319                  int stride = BitUtils.AlignUp(Math.Max(1, lhs.Stride >> lhsLevel), Constants.StrideAlignment);
320  
321                  return stride == rhs.Stride;
322              }
323              else
324              {
325                  return false;
326              }
327          }
328  
329          /// <summary>
330          /// Checks if the sizes of two given textures are view compatible.
331          /// </summary>
332          /// <param name="lhs">Texture information of the texture view</param>
333          /// <param name="rhs">Texture information of the texture view to match against</param>
334          /// <param name="exact">Indicates if the sizes must be exactly equal</param>
335          /// <param name="level">Mipmap level of the texture view in relation to this texture</param>
336          /// <returns>The view compatibility level of the view sizes</returns>
337          public static TextureViewCompatibility ViewSizeMatches(TextureInfo lhs, TextureInfo rhs, bool exact, int level)
338          {
339              Size lhsAlignedSize = GetAlignedSize(lhs, level);
340              Size rhsAlignedSize = GetAlignedSize(rhs);
341  
342              Size lhsSize = GetSizeInBlocks(lhs, level);
343              Size rhsSize = GetSizeInBlocks(rhs);
344  
345              bool alignedWidthMatches = lhsAlignedSize.Width == rhsAlignedSize.Width;
346  
347              if (lhs.FormatInfo.BytesPerPixel != rhs.FormatInfo.BytesPerPixel && IsIncompatibleFormatAliasingAllowed(lhs.FormatInfo, rhs.FormatInfo))
348              {
349                  // If the formats are incompatible, but the texture strides match,
350                  // we might allow them to be copy compatible depending on the format.
351                  // The strides are aligned because the format with higher bytes per pixel
352                  // might need a bit of padding at the end due to one width not being a multiple of the other.
353  
354                  Debug.Assert((1 << BitOperations.Log2((uint)lhs.FormatInfo.BytesPerPixel)) == lhs.FormatInfo.BytesPerPixel);
355                  Debug.Assert((1 << BitOperations.Log2((uint)rhs.FormatInfo.BytesPerPixel)) == rhs.FormatInfo.BytesPerPixel);
356  
357                  int alignment = Math.Max(lhs.FormatInfo.BytesPerPixel, rhs.FormatInfo.BytesPerPixel);
358  
359                  int lhsStride = BitUtils.AlignUp(lhsSize.Width * lhs.FormatInfo.BytesPerPixel, alignment);
360                  int rhsStride = BitUtils.AlignUp(rhsSize.Width * rhs.FormatInfo.BytesPerPixel, alignment);
361  
362                  alignedWidthMatches = lhsStride == rhsStride;
363              }
364  
365              TextureViewCompatibility result = TextureViewCompatibility.Full;
366  
367              // For copies, we can copy a subset of the 3D texture slices,
368              // so the depth may be different in this case.
369              if (rhs.Target == Target.Texture3D && lhsSize.Depth != rhsSize.Depth)
370              {
371                  result = TextureViewCompatibility.CopyOnly;
372              }
373  
374              // Some APIs align the width for copy and render target textures,
375              // so the width may not match in this case for different uses of the same texture.
376              // To account for this, we compare the aligned width here.
377              // We expect height to always match exactly, if the texture is the same.
378              if (alignedWidthMatches && lhsSize.Height == rhsSize.Height)
379              {
380                  return (exact && lhsSize.Width != rhsSize.Width) || lhsSize.Width < rhsSize.Width
381                      ? TextureViewCompatibility.CopyOnly
382                      : result;
383              }
384              else if (lhs.IsLinear && rhs.IsLinear && lhsSize.Height == rhsSize.Height)
385              {
386                  // Copy between linear textures with matching stride.
387                  int stride = BitUtils.AlignUp(Math.Max(1, lhs.Stride >> level), Constants.StrideAlignment);
388  
389                  return stride == rhs.Stride ? TextureViewCompatibility.CopyOnly : TextureViewCompatibility.LayoutIncompatible;
390              }
391              else if (lhs.Target.IsMultisample() != rhs.Target.IsMultisample() && alignedWidthMatches && lhsAlignedSize.Height == rhsAlignedSize.Height)
392              {
393                  // Copy between multisample and non-multisample textures with mismatching size is allowed,
394                  // as long aligned size matches.
395  
396                  return TextureViewCompatibility.CopyOnly;
397              }
398              else
399              {
400                  return TextureViewCompatibility.LayoutIncompatible;
401              }
402          }
403  
404          /// <summary>
405          /// Checks if the potential child texture fits within the level and layer bounds of the parent.
406          /// </summary>
407          /// <param name="parent">Texture information for the parent</param>
408          /// <param name="child">Texture information for the child</param>
409          /// <param name="layer">Base layer of the child texture</param>
410          /// <param name="level">Base level of the child texture</param>
411          /// <returns>Full compatiblity if the child's layer and level count fit within the parent, incompatible otherwise</returns>
412          public static TextureViewCompatibility ViewSubImagesInBounds(TextureInfo parent, TextureInfo child, int layer, int level)
413          {
414              if (level + child.Levels <= parent.Levels &&
415                  layer + child.GetSlices() <= parent.GetSlices())
416              {
417                  return TextureViewCompatibility.Full;
418              }
419              else
420              {
421                  return TextureViewCompatibility.LayoutIncompatible;
422              }
423          }
424  
425          /// <summary>
426          /// Checks if the texture sizes of the supplied texture informations match.
427          /// </summary>
428          /// <param name="lhs">Texture information to compare</param>
429          /// <param name="rhs">Texture information to compare with</param>
430          /// <param name="exact">Indicates if the size must be exactly equal between the textures, or if <paramref name="rhs"/> is allowed to be larger</param>
431          /// <returns>True if the sizes matches, false otherwise</returns>
432          public static bool SizeMatches(TextureInfo lhs, TextureInfo rhs, bool exact)
433          {
434              if (lhs.GetLayers() != rhs.GetLayers())
435              {
436                  return false;
437              }
438  
439              Size lhsSize = GetSizeInBlocks(lhs);
440              Size rhsSize = GetSizeInBlocks(rhs);
441  
442              if (exact || lhs.IsLinear || rhs.IsLinear)
443              {
444                  return lhsSize.Width == rhsSize.Width &&
445                         lhsSize.Height == rhsSize.Height &&
446                         lhsSize.Depth == rhsSize.Depth;
447              }
448              else
449              {
450                  Size lhsAlignedSize = GetAlignedSize(lhs);
451                  Size rhsAlignedSize = GetAlignedSize(rhs);
452  
453                  return lhsAlignedSize.Width == rhsAlignedSize.Width &&
454                         lhsSize.Width >= rhsSize.Width &&
455                         lhsSize.Height == rhsSize.Height &&
456                         lhsSize.Depth == rhsSize.Depth;
457              }
458          }
459  
460          /// <summary>
461          /// Gets the aligned sizes for the given dimensions, using the specified texture information.
462          /// The alignment depends on the texture layout and format bytes per pixel.
463          /// </summary>
464          /// <param name="info">Texture information to calculate the aligned size from</param>
465          /// <param name="width">The width to be aligned</param>
466          /// <param name="height">The height to be aligned</param>
467          /// <param name="depth">The depth to be aligned</param>
468          /// <returns>The aligned texture size</returns>
469          private static Size GetAlignedSize(TextureInfo info, int width, int height, int depth)
470          {
471              if (info.IsLinear)
472              {
473                  return SizeCalculator.GetLinearAlignedSize(
474                      width,
475                      height,
476                      info.FormatInfo.BlockWidth,
477                      info.FormatInfo.BlockHeight,
478                      info.FormatInfo.BytesPerPixel);
479              }
480              else
481              {
482                  return SizeCalculator.GetBlockLinearAlignedSize(
483                      width,
484                      height,
485                      depth,
486                      info.FormatInfo.BlockWidth,
487                      info.FormatInfo.BlockHeight,
488                      info.FormatInfo.BytesPerPixel,
489                      info.GobBlocksInY,
490                      info.GobBlocksInZ,
491                      info.GobBlocksInTileX);
492              }
493          }
494  
495          /// <summary>
496          /// Gets the aligned sizes of the specified texture information.
497          /// The alignment depends on the texture layout and format bytes per pixel.
498          /// </summary>
499          /// <param name="info">Texture information to calculate the aligned size from</param>
500          /// <param name="level">Mipmap level for texture views</param>
501          /// <returns>The aligned texture size</returns>
502          public static Size GetAlignedSize(TextureInfo info, int level = 0)
503          {
504              int width = Math.Max(1, info.Width >> level);
505              int height = Math.Max(1, info.Height >> level);
506              int depth = Math.Max(1, info.GetDepth() >> level);
507  
508              return GetAlignedSize(info, width, height, depth);
509          }
510  
511          /// <summary>
512          /// Gets the size in blocks for the given texture information.
513          /// For non-compressed formats, that's the same as the regular size.
514          /// </summary>
515          /// <param name="info">Texture information to calculate the aligned size from</param>
516          /// <param name="level">Mipmap level for texture views</param>
517          /// <returns>The texture size in blocks</returns>
518          public static Size GetSizeInBlocks(TextureInfo info, int level = 0)
519          {
520              int width = Math.Max(1, info.Width >> level);
521              int height = Math.Max(1, info.Height >> level);
522              int depth = Math.Max(1, info.GetDepth() >> level);
523  
524              return new Size(
525                  BitUtils.DivRoundUp(width, info.FormatInfo.BlockWidth),
526                  BitUtils.DivRoundUp(height, info.FormatInfo.BlockHeight),
527                  depth);
528          }
529  
530          /// <summary>
531          /// Check if it's possible to create a view with the layout of the second texture information from the first.
532          /// The layout information is composed of the Stride for linear textures, or GOB block size
533          /// for block linear textures.
534          /// </summary>
535          /// <param name="lhs">Texture information of the texture view</param>
536          /// <param name="rhs">Texture information of the texture view to compare against</param>
537          /// <param name="level">Start level of the texture view, in relation with the first texture</param>
538          /// <returns>True if the layout is compatible, false otherwise</returns>
539          public static bool ViewLayoutCompatible(TextureInfo lhs, TextureInfo rhs, int level)
540          {
541              if (lhs.IsLinear != rhs.IsLinear)
542              {
543                  return false;
544              }
545  
546              // For linear textures, gob block sizes are ignored.
547              // For block linear textures, the stride is ignored.
548              if (rhs.IsLinear)
549              {
550                  int stride = Math.Max(1, lhs.Stride >> level);
551                  stride = BitUtils.AlignUp(stride, Constants.StrideAlignment);
552  
553                  return stride == rhs.Stride;
554              }
555              else
556              {
557                  int height = Math.Max(1, lhs.Height >> level);
558                  int depth = Math.Max(1, lhs.GetDepth() >> level);
559  
560                  (int gobBlocksInY, int gobBlocksInZ) = SizeCalculator.GetMipGobBlockSizes(
561                      height,
562                      depth,
563                      lhs.FormatInfo.BlockHeight,
564                      lhs.GobBlocksInY,
565                      lhs.GobBlocksInZ,
566                      level);
567  
568                  return gobBlocksInY == rhs.GobBlocksInY &&
569                         gobBlocksInZ == rhs.GobBlocksInZ;
570              }
571          }
572  
573          /// <summary>
574          /// Check if it's possible to create a view with the layout of the second texture information from the first.
575          /// The layout information is composed of the Stride for linear textures, or GOB block size
576          /// for block linear textures.
577          /// </summary>
578          /// <param name="lhs">Texture information of the texture view</param>
579          /// <param name="rhs">Texture information of the texture view to compare against</param>
580          /// <param name="lhsLevel">Start level of the texture view, in relation with the first texture</param>
581          /// <param name="rhsLevel">Start level of the texture view, in relation with the second texture</param>
582          /// <returns>True if the layout is compatible, false otherwise</returns>
583          public static bool ViewLayoutCompatible(TextureInfo lhs, TextureInfo rhs, int lhsLevel, int rhsLevel)
584          {
585              if (lhs.IsLinear != rhs.IsLinear)
586              {
587                  return false;
588              }
589  
590              // For linear textures, gob block sizes are ignored.
591              // For block linear textures, the stride is ignored.
592              if (rhs.IsLinear)
593              {
594                  int lhsStride = Math.Max(1, lhs.Stride >> lhsLevel);
595                  lhsStride = BitUtils.AlignUp(lhsStride, Constants.StrideAlignment);
596  
597                  int rhsStride = Math.Max(1, rhs.Stride >> rhsLevel);
598                  rhsStride = BitUtils.AlignUp(rhsStride, Constants.StrideAlignment);
599  
600                  return lhsStride == rhsStride;
601              }
602              else
603              {
604                  int lhsHeight = Math.Max(1, lhs.Height >> lhsLevel);
605                  int lhsDepth = Math.Max(1, lhs.GetDepth() >> lhsLevel);
606  
607                  (int lhsGobBlocksInY, int lhsGobBlocksInZ) = SizeCalculator.GetMipGobBlockSizes(
608                      lhsHeight,
609                      lhsDepth,
610                      lhs.FormatInfo.BlockHeight,
611                      lhs.GobBlocksInY,
612                      lhs.GobBlocksInZ,
613                      lhsLevel);
614  
615                  int rhsHeight = Math.Max(1, rhs.Height >> rhsLevel);
616                  int rhsDepth = Math.Max(1, rhs.GetDepth() >> rhsLevel);
617  
618                  (int rhsGobBlocksInY, int rhsGobBlocksInZ) = SizeCalculator.GetMipGobBlockSizes(
619                      rhsHeight,
620                      rhsDepth,
621                      rhs.FormatInfo.BlockHeight,
622                      rhs.GobBlocksInY,
623                      rhs.GobBlocksInZ,
624                      rhsLevel);
625  
626                  return lhsGobBlocksInY == rhsGobBlocksInY &&
627                         lhsGobBlocksInZ == rhsGobBlocksInZ;
628              }
629          }
630  
631          /// <summary>
632          /// Checks if the view format of the first texture format is compatible with the format of the second.
633          /// In general, the formats are considered compatible if the bytes per pixel values are equal,
634          /// but there are more complex rules for some formats, like compressed or depth-stencil formats.
635          /// This follows the host API copy compatibility rules.
636          /// </summary>
637          /// <param name="lhs">Texture information of the texture view</param>
638          /// <param name="rhs">Texture information of the texture view</param>
639          /// <param name="caps">Host GPU capabilities</param>
640          /// <param name="flags">Texture search flags</param>
641          /// <returns>The view compatibility level of the texture formats</returns>
642          public static TextureViewCompatibility ViewFormatCompatible(TextureInfo lhs, TextureInfo rhs, Capabilities caps, TextureSearchFlags flags)
643          {
644              FormatInfo lhsFormat = lhs.FormatInfo;
645              FormatInfo rhsFormat = rhs.FormatInfo;
646  
647              if (lhsFormat.Format.IsDepthOrStencil() || rhsFormat.Format.IsDepthOrStencil())
648              {
649                  bool forSampler = flags.HasFlag(TextureSearchFlags.ForSampler);
650                  bool depthAlias = flags.HasFlag(TextureSearchFlags.DepthAlias);
651  
652                  TextureMatchQuality matchQuality = FormatMatches(lhs, rhs, forSampler, depthAlias);
653  
654                  if (matchQuality == TextureMatchQuality.Perfect)
655                  {
656                      return TextureViewCompatibility.Full;
657                  }
658                  else if (matchQuality == TextureMatchQuality.FormatAlias)
659                  {
660                      return TextureViewCompatibility.FormatAlias;
661                  }
662                  else if (IsValidColorAsDepthAlias(lhsFormat.Format, rhsFormat.Format) || IsValidDepthAsColorAlias(lhsFormat.Format, rhsFormat.Format))
663                  {
664                      return TextureViewCompatibility.CopyOnly;
665                  }
666                  else
667                  {
668                      return TextureViewCompatibility.Incompatible;
669                  }
670              }
671  
672              if (IsFormatHostIncompatible(lhs, caps) || IsFormatHostIncompatible(rhs, caps))
673              {
674                  return lhsFormat.Format == rhsFormat.Format ? TextureViewCompatibility.Full : TextureViewCompatibility.Incompatible;
675              }
676  
677              if (lhsFormat.IsCompressed && rhsFormat.IsCompressed)
678              {
679                  FormatClass lhsClass = GetFormatClass(lhsFormat.Format);
680                  FormatClass rhsClass = GetFormatClass(rhsFormat.Format);
681  
682                  return lhsClass == rhsClass ? TextureViewCompatibility.Full : TextureViewCompatibility.Incompatible;
683              }
684              else if (lhsFormat.BytesPerPixel == rhsFormat.BytesPerPixel)
685              {
686                  return lhs.FormatInfo.IsCompressed == rhs.FormatInfo.IsCompressed
687                      ? TextureViewCompatibility.Full
688                      : TextureViewCompatibility.CopyOnly;
689              }
690              else if (IsIncompatibleFormatAliasingAllowed(lhsFormat, rhsFormat))
691              {
692                  return TextureViewCompatibility.CopyOnly;
693              }
694  
695              return TextureViewCompatibility.Incompatible;
696          }
697  
698          /// <summary>
699          /// Checks if it's valid to alias a color format as a depth format.
700          /// </summary>
701          /// <param name="lhsFormat">Source format to be checked</param>
702          /// <param name="rhsFormat">Target format to be checked</param>
703          /// <returns>True if it's valid to alias the formats</returns>
704          private static bool IsValidColorAsDepthAlias(Format lhsFormat, Format rhsFormat)
705          {
706              return (lhsFormat == Format.R32Float && rhsFormat == Format.D32Float) ||
707                     (lhsFormat == Format.R16Unorm && rhsFormat == Format.D16Unorm);
708          }
709  
710          /// <summary>
711          /// Checks if it's valid to alias a depth format as a color format.
712          /// </summary>
713          /// <param name="lhsFormat">Source format to be checked</param>
714          /// <param name="rhsFormat">Target format to be checked</param>
715          /// <returns>True if it's valid to alias the formats</returns>
716          private static bool IsValidDepthAsColorAlias(Format lhsFormat, Format rhsFormat)
717          {
718              return (lhsFormat == Format.D32Float && rhsFormat == Format.R32Float) ||
719                     (lhsFormat == Format.D16Unorm && rhsFormat == Format.R16Unorm);
720          }
721  
722          /// <summary>
723          /// Checks if aliasing of two formats that would normally be considered incompatible be allowed,
724          /// using copy dependencies.
725          /// </summary>
726          /// <param name="lhsFormat">Format information of the first texture</param
727          /// <param name="rhsFormat">Format information of the second texture</param>
728          /// <returns>True if aliasing should be allowed, false otherwise</returns>
729          private static bool IsIncompatibleFormatAliasingAllowed(FormatInfo lhsFormat, FormatInfo rhsFormat)
730          {
731              // Some games will try to alias textures with incompatible foramts, with different BPP (bytes per pixel).
732              // We allow that in some cases as long Width * BPP is equal on both textures.
733              // This is very conservative right now as we want to avoid copies as much as possible,
734              // so we only consider the formats we have seen being aliased.
735  
736              if (rhsFormat.BytesPerPixel < lhsFormat.BytesPerPixel)
737              {
738                  (lhsFormat, rhsFormat) = (rhsFormat, lhsFormat);
739              }
740  
741              return (lhsFormat.Format == Format.R8G8B8A8Unorm && rhsFormat.Format == Format.R32G32B32A32Float) ||
742                     (lhsFormat.Format == Format.R8Unorm && rhsFormat.Format == Format.R8G8B8A8Unorm) ||
743                     (lhsFormat.Format == Format.R8Unorm && rhsFormat.Format == Format.R32Uint);
744          }
745  
746          /// <summary>
747          /// Check if the target of the first texture view information is compatible with the target of the second texture view information.
748          /// This follows the host API target compatibility rules.
749          /// </summary>
750          /// <param name="lhs">Texture information of the texture view</param
751          /// <param name="rhs">Texture information of the texture view</param>
752          /// <param name="caps">Host GPU capabilities</param>
753          /// <returns>True if the targets are compatible, false otherwise</returns>
754          public static TextureViewCompatibility ViewTargetCompatible(TextureInfo lhs, TextureInfo rhs, ref Capabilities caps)
755          {
756              bool result = false;
757              switch (lhs.Target)
758              {
759                  case Target.Texture1D:
760                  case Target.Texture1DArray:
761                      result = rhs.Target == Target.Texture1D ||
762                               rhs.Target == Target.Texture1DArray;
763                      break;
764  
765                  case Target.Texture2D:
766                      result = rhs.Target == Target.Texture2D ||
767                               rhs.Target == Target.Texture2DArray;
768                      break;
769  
770                  case Target.Texture2DArray:
771                      result = rhs.Target == Target.Texture2D ||
772                               rhs.Target == Target.Texture2DArray;
773  
774                      if (rhs.Target == Target.Cubemap || rhs.Target == Target.CubemapArray)
775                      {
776                          return caps.SupportsCubemapView ? TextureViewCompatibility.Full : TextureViewCompatibility.CopyOnly;
777                      }
778                      break;
779                  case Target.Cubemap:
780                  case Target.CubemapArray:
781                      result = rhs.Target == Target.Cubemap ||
782                               rhs.Target == Target.CubemapArray;
783  
784                      if (rhs.Target == Target.Texture2D || rhs.Target == Target.Texture2DArray)
785                      {
786                          return caps.SupportsCubemapView ? TextureViewCompatibility.Full : TextureViewCompatibility.CopyOnly;
787                      }
788                      break;
789                  case Target.Texture2DMultisample:
790                  case Target.Texture2DMultisampleArray:
791                      if (rhs.Target == Target.Texture2D || rhs.Target == Target.Texture2DArray)
792                      {
793                          return TextureViewCompatibility.CopyOnly;
794                      }
795  
796                      result = rhs.Target == Target.Texture2DMultisample ||
797                               rhs.Target == Target.Texture2DMultisampleArray;
798                      break;
799  
800                  case Target.Texture3D:
801                      if (rhs.Target == Target.Texture2D)
802                      {
803                          return TextureViewCompatibility.CopyOnly;
804                      }
805  
806                      result = rhs.Target == Target.Texture3D;
807                      break;
808              }
809  
810              return result ? TextureViewCompatibility.Full : TextureViewCompatibility.Incompatible;
811          }
812  
813          /// <summary>
814          /// Checks if the texture shader sampling parameters of two texture informations match.
815          /// </summary>
816          /// <param name="lhs">Texture information to compare</param>
817          /// <param name="rhs">Texture information to compare with</param>
818          /// <returns>True if the texture shader sampling parameters matches, false otherwise</returns>
819          public static bool SamplerParamsMatches(TextureInfo lhs, TextureInfo rhs)
820          {
821              return lhs.DepthStencilMode == rhs.DepthStencilMode &&
822                     lhs.SwizzleR == rhs.SwizzleR &&
823                     lhs.SwizzleG == rhs.SwizzleG &&
824                     lhs.SwizzleB == rhs.SwizzleB &&
825                     lhs.SwizzleA == rhs.SwizzleA;
826          }
827  
828          /// <summary>
829          /// Check if the texture target and samples count (for multisampled textures) matches.
830          /// </summary>
831          /// <param name="first">Texture information to compare with</param>
832          /// <param name="rhs">Texture information to compare with</param>
833          /// <returns>True if the texture target and samples count matches, false otherwise</returns>
834          public static bool TargetAndSamplesCompatible(TextureInfo lhs, TextureInfo rhs)
835          {
836              return lhs.Target == rhs.Target &&
837                     lhs.SamplesInX == rhs.SamplesInX &&
838                     lhs.SamplesInY == rhs.SamplesInY;
839          }
840  
841          /// <summary>
842          /// Gets the texture format class, for compressed textures, or Unclassified otherwise.
843          /// </summary>
844          /// <param name="format">The format</param>
845          /// <returns>Format class</returns>
846          private static FormatClass GetFormatClass(Format format)
847          {
848              return format switch
849              {
850                  Format.Bc1RgbaSrgb or Format.Bc1RgbaUnorm => FormatClass.Bc1Rgba,
851                  Format.Bc2Srgb or Format.Bc2Unorm => FormatClass.Bc2,
852                  Format.Bc3Srgb or Format.Bc3Unorm => FormatClass.Bc3,
853                  Format.Bc4Snorm or Format.Bc4Unorm => FormatClass.Bc4,
854                  Format.Bc5Snorm or Format.Bc5Unorm => FormatClass.Bc5,
855                  Format.Bc6HSfloat or Format.Bc6HUfloat => FormatClass.Bc6,
856                  Format.Bc7Srgb or Format.Bc7Unorm => FormatClass.Bc7,
857                  Format.Etc2RgbSrgb or Format.Etc2RgbUnorm => FormatClass.Etc2Rgb,
858                  Format.Etc2RgbaSrgb or Format.Etc2RgbaUnorm => FormatClass.Etc2Rgba,
859                  Format.Astc4x4Srgb or Format.Astc4x4Unorm => FormatClass.Astc4x4,
860                  Format.Astc5x4Srgb or Format.Astc5x4Unorm => FormatClass.Astc5x4,
861                  Format.Astc5x5Srgb or Format.Astc5x5Unorm => FormatClass.Astc5x5,
862                  Format.Astc6x5Srgb or Format.Astc6x5Unorm => FormatClass.Astc6x5,
863                  Format.Astc6x6Srgb or Format.Astc6x6Unorm => FormatClass.Astc6x6,
864                  Format.Astc8x5Srgb or Format.Astc8x5Unorm => FormatClass.Astc8x5,
865                  Format.Astc8x6Srgb or Format.Astc8x6Unorm => FormatClass.Astc8x6,
866                  Format.Astc8x8Srgb or Format.Astc8x8Unorm => FormatClass.Astc8x8,
867                  Format.Astc10x5Srgb or Format.Astc10x5Unorm => FormatClass.Astc10x5,
868                  Format.Astc10x6Srgb or Format.Astc10x6Unorm => FormatClass.Astc10x6,
869                  Format.Astc10x8Srgb or Format.Astc10x8Unorm => FormatClass.Astc10x8,
870                  Format.Astc10x10Srgb or Format.Astc10x10Unorm => FormatClass.Astc10x10,
871                  Format.Astc12x10Srgb or Format.Astc12x10Unorm => FormatClass.Astc12x10,
872                  Format.Astc12x12Srgb or Format.Astc12x12Unorm => FormatClass.Astc12x12,
873                  _ => FormatClass.Unclassified,
874              };
875          }
876      }
877  }