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 }