/ src / Ryujinx.Graphics.Gpu / Image / TextureCache.cs
TextureCache.cs
   1  using Ryujinx.Common;
   2  using Ryujinx.Graphics.GAL;
   3  using Ryujinx.Graphics.Gpu.Engine.Threed;
   4  using Ryujinx.Graphics.Gpu.Engine.Twod;
   5  using Ryujinx.Graphics.Gpu.Engine.Types;
   6  using Ryujinx.Graphics.Gpu.Memory;
   7  using Ryujinx.Graphics.Texture;
   8  using Ryujinx.Memory.Range;
   9  using System;
  10  using System.Collections.Generic;
  11  using System.Threading;
  12  
  13  namespace Ryujinx.Graphics.Gpu.Image
  14  {
  15      /// <summary>
  16      /// Texture cache.
  17      /// </summary>
  18      class TextureCache : IDisposable
  19      {
  20          private readonly struct OverlapInfo
  21          {
  22              public TextureViewCompatibility Compatibility { get; }
  23              public int FirstLayer { get; }
  24              public int FirstLevel { get; }
  25  
  26              public OverlapInfo(TextureViewCompatibility compatibility, int firstLayer, int firstLevel)
  27              {
  28                  Compatibility = compatibility;
  29                  FirstLayer = firstLayer;
  30                  FirstLevel = firstLevel;
  31              }
  32          }
  33  
  34          private const int OverlapsBufferInitialCapacity = 10;
  35          private const int OverlapsBufferMaxCapacity = 10000;
  36  
  37          private readonly GpuContext _context;
  38          private readonly PhysicalMemory _physicalMemory;
  39  
  40          private readonly MultiRangeList<Texture> _textures;
  41          private readonly HashSet<Texture> _partiallyMappedTextures;
  42  
  43          private readonly ReaderWriterLockSlim _texturesLock;
  44  
  45          private Texture[] _textureOverlaps;
  46          private OverlapInfo[] _overlapInfo;
  47  
  48          private readonly AutoDeleteCache _cache;
  49  
  50          /// <summary>
  51          /// Constructs a new instance of the texture manager.
  52          /// </summary>
  53          /// <param name="context">The GPU context that the texture manager belongs to</param>
  54          /// <param name="physicalMemory">Physical memory where the textures managed by this cache are mapped</param>
  55          public TextureCache(GpuContext context, PhysicalMemory physicalMemory)
  56          {
  57              _context = context;
  58              _physicalMemory = physicalMemory;
  59  
  60              _textures = new MultiRangeList<Texture>();
  61              _partiallyMappedTextures = new HashSet<Texture>();
  62  
  63              _texturesLock = new ReaderWriterLockSlim();
  64  
  65              _textureOverlaps = new Texture[OverlapsBufferInitialCapacity];
  66              _overlapInfo = new OverlapInfo[OverlapsBufferInitialCapacity];
  67  
  68              _cache = new AutoDeleteCache();
  69          }
  70  
  71          /// <summary>
  72          /// Initializes the cache, setting the maximum texture capacity for the specified GPU context.
  73          /// </summary>
  74          public void Initialize()
  75          {
  76              _cache.Initialize(_context);
  77          }
  78  
  79          /// <summary>
  80          /// Handles marking of textures written to a memory region being (partially) remapped.
  81          /// </summary>
  82          /// <param name="sender">Sender object</param>
  83          /// <param name="e">Event arguments</param>
  84          public void MemoryUnmappedHandler(object sender, UnmapEventArgs e)
  85          {
  86              Texture[] overlaps = new Texture[10];
  87              int overlapCount;
  88  
  89              MultiRange unmapped = ((MemoryManager)sender).GetPhysicalRegions(e.Address, e.Size);
  90  
  91              _texturesLock.EnterReadLock();
  92  
  93              try
  94              {
  95                  overlapCount = _textures.FindOverlaps(unmapped, ref overlaps);
  96              }
  97              finally
  98              {
  99                  _texturesLock.ExitReadLock();
 100              }
 101  
 102              if (overlapCount > 0)
 103              {
 104                  for (int i = 0; i < overlapCount; i++)
 105                  {
 106                      overlaps[i].Unmapped(unmapped);
 107                  }
 108              }
 109  
 110              lock (_partiallyMappedTextures)
 111              {
 112                  if (overlapCount > 0 || _partiallyMappedTextures.Count > 0)
 113                  {
 114                      e.AddRemapAction(() =>
 115                      {
 116                          lock (_partiallyMappedTextures)
 117                          {
 118                              if (overlapCount > 0)
 119                              {
 120                                  for (int i = 0; i < overlapCount; i++)
 121                                  {
 122                                      _partiallyMappedTextures.Add(overlaps[i]);
 123                                  }
 124                              }
 125  
 126                              // Any texture that has been unmapped at any point or is partially unmapped
 127                              // should update their pool references after the remap completes.
 128  
 129                              foreach (var texture in _partiallyMappedTextures)
 130                              {
 131                                  texture.UpdatePoolMappings();
 132                              }
 133                          }
 134                      });
 135                  }
 136              }
 137          }
 138  
 139          /// <summary>
 140          /// Determines if a given texture is eligible for upscaling from its info.
 141          /// </summary>
 142          /// <param name="info">The texture info to check</param>
 143          /// <param name="withUpscale">True if the user of the texture would prefer it to be upscaled immediately</param>
 144          /// <returns>True if eligible</returns>
 145          private static TextureScaleMode IsUpscaleCompatible(TextureInfo info, bool withUpscale)
 146          {
 147              if ((info.Target == Target.Texture2D || info.Target == Target.Texture2DArray || info.Target == Target.Texture2DMultisample) && !info.FormatInfo.IsCompressed)
 148              {
 149                  return UpscaleSafeMode(info) ? (withUpscale ? TextureScaleMode.Scaled : TextureScaleMode.Eligible) : TextureScaleMode.Undesired;
 150              }
 151  
 152              return TextureScaleMode.Blacklisted;
 153          }
 154  
 155          /// <summary>
 156          /// Determines if a given texture is "safe" for upscaling from its info.
 157          /// Note that this is different from being compatible - this elilinates targets that would have detrimental effects when scaled.
 158          /// </summary>
 159          /// <param name="info">The texture info to check</param>
 160          /// <returns>True if safe</returns>
 161          private static bool UpscaleSafeMode(TextureInfo info)
 162          {
 163              // While upscaling works for all targets defined by IsUpscaleCompatible, we additionally blacklist targets here that
 164              // may have undesirable results (upscaling blur textures) or simply waste GPU resources (upscaling texture atlas).
 165  
 166              if (info.Levels > 3)
 167              {
 168                  // Textures with more than 3 levels are likely to be game textures, rather than render textures.
 169                  // Small textures with full mips are likely to be removed by the next check.
 170                  return false;
 171              }
 172  
 173              if (info.Width < 8 || info.Height < 8)
 174              {
 175                  // Discount textures with small dimensions.
 176                  return false;
 177              }
 178  
 179              int widthAlignment = (info.IsLinear ? Constants.StrideAlignment : Constants.GobAlignment) / info.FormatInfo.BytesPerPixel;
 180  
 181              if (!(info.FormatInfo.Format.IsDepthOrStencil() || info.FormatInfo.Components == 1))
 182              {
 183                  // Discount square textures that aren't depth-stencil like. (excludes game textures, cubemap faces, most 3D texture LUT, texture atlas)
 184                  // Detect if the texture is possibly square. Widths may be aligned, so to remove the uncertainty we align both the width and height.
 185  
 186                  bool possiblySquare = BitUtils.AlignUp(info.Width, widthAlignment) == BitUtils.AlignUp(info.Height, widthAlignment);
 187  
 188                  if (possiblySquare)
 189                  {
 190                      return false;
 191                  }
 192              }
 193  
 194              if (info.Height < 360)
 195              {
 196                  int aspectWidth = (int)MathF.Ceiling((info.Height / 9f) * 16f);
 197                  int aspectMaxWidth = BitUtils.AlignUp(aspectWidth, widthAlignment);
 198                  int aspectMinWidth = BitUtils.AlignDown(aspectWidth, widthAlignment);
 199  
 200                  if (info.Width >= aspectMinWidth && info.Width <= aspectMaxWidth && info.Height < 360)
 201                  {
 202                      // Targets that are roughly 16:9 can only be rescaled if they're equal to or above 360p. (excludes blur and bloom textures)
 203                      return false;
 204                  }
 205              }
 206  
 207              if (info.Width == info.Height * info.Height)
 208              {
 209                  // Possibly used for a "3D texture" drawn onto a 2D surface.
 210                  // Some games do this to generate a tone mapping LUT without rendering into 3D texture slices.
 211  
 212                  return false;
 213              }
 214  
 215              return true;
 216          }
 217  
 218          /// <summary>
 219          /// Lifts the texture to the top of the AutoDeleteCache. This is primarily used to enforce that
 220          /// data written to a target will be flushed to memory should the texture be deleted, but also
 221          /// keeps rendered textures alive without a pool reference.
 222          /// </summary>
 223          /// <param name="texture">Texture to lift</param>
 224          public void Lift(Texture texture)
 225          {
 226              _cache.Lift(texture);
 227          }
 228  
 229          /// <summary>
 230          /// Attempts to update a texture's physical memory range.
 231          /// Returns false if there is an existing texture that matches with the updated range.
 232          /// </summary>
 233          /// <param name="texture">Texture to update</param>
 234          /// <param name="range">New physical memory range</param>
 235          /// <returns>True if the mapping was updated, false otherwise</returns>
 236          public bool UpdateMapping(Texture texture, MultiRange range)
 237          {
 238              // There cannot be an existing texture compatible with this mapping in the texture cache already.
 239              int overlapCount;
 240  
 241              _texturesLock.EnterReadLock();
 242  
 243              try
 244              {
 245                  overlapCount = _textures.FindOverlaps(range, ref _textureOverlaps);
 246              }
 247              finally
 248              {
 249                  _texturesLock.ExitReadLock();
 250              }
 251  
 252              for (int i = 0; i < overlapCount; i++)
 253              {
 254                  var other = _textureOverlaps[i];
 255  
 256                  if (texture != other &&
 257                      (texture.IsViewCompatible(other.Info, other.Range, true, other.LayerSize, _context.Capabilities, out _, out _) != TextureViewCompatibility.Incompatible ||
 258                      other.IsViewCompatible(texture.Info, texture.Range, true, texture.LayerSize, _context.Capabilities, out _, out _) != TextureViewCompatibility.Incompatible))
 259                  {
 260                      return false;
 261                  }
 262              }
 263  
 264              _texturesLock.EnterWriteLock();
 265  
 266              try
 267              {
 268                  _textures.Remove(texture);
 269  
 270                  texture.ReplaceRange(range);
 271  
 272                  _textures.Add(texture);
 273              }
 274              finally
 275              {
 276                  _texturesLock.ExitWriteLock();
 277              }
 278  
 279              return true;
 280          }
 281  
 282          /// <summary>
 283          /// Tries to find an existing texture, or create a new one if not found.
 284          /// </summary>
 285          /// <param name="memoryManager">GPU memory manager where the texture is mapped</param>
 286          /// <param name="copyTexture">Copy texture to find or create</param>
 287          /// <param name="offset">Offset to be added to the physical texture address</param>
 288          /// <param name="formatInfo">Format information of the copy texture</param>
 289          /// <param name="depthAlias">Indicates if aliasing between color and depth format should be allowed</param>
 290          /// <param name="shouldCreate">Indicates if a new texture should be created if none is found on the cache</param>
 291          /// <param name="preferScaling">Indicates if the texture should be scaled from the start</param>
 292          /// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
 293          /// <returns>The texture</returns>
 294          public Texture FindOrCreateTexture(
 295              MemoryManager memoryManager,
 296              TwodTexture copyTexture,
 297              ulong offset,
 298              FormatInfo formatInfo,
 299              bool depthAlias,
 300              bool shouldCreate,
 301              bool preferScaling,
 302              Size sizeHint)
 303          {
 304              int gobBlocksInY = copyTexture.MemoryLayout.UnpackGobBlocksInY();
 305              int gobBlocksInZ = copyTexture.MemoryLayout.UnpackGobBlocksInZ();
 306  
 307              int width;
 308  
 309              if (copyTexture.LinearLayout)
 310              {
 311                  width = copyTexture.Stride / formatInfo.BytesPerPixel;
 312              }
 313              else
 314              {
 315                  width = copyTexture.Width;
 316              }
 317  
 318              TextureInfo info = new(
 319                  copyTexture.Address.Pack() + offset,
 320                  GetMinimumWidthInGob(width, sizeHint.Width, formatInfo.BytesPerPixel, copyTexture.LinearLayout),
 321                  copyTexture.Height,
 322                  copyTexture.Depth,
 323                  1,
 324                  1,
 325                  1,
 326                  copyTexture.Stride,
 327                  copyTexture.LinearLayout,
 328                  gobBlocksInY,
 329                  gobBlocksInZ,
 330                  1,
 331                  Target.Texture2D,
 332                  formatInfo);
 333  
 334              TextureSearchFlags flags = TextureSearchFlags.ForCopy;
 335  
 336              if (depthAlias)
 337              {
 338                  flags |= TextureSearchFlags.DepthAlias;
 339              }
 340  
 341              if (preferScaling)
 342              {
 343                  flags |= TextureSearchFlags.WithUpscale;
 344              }
 345  
 346              if (!shouldCreate)
 347              {
 348                  flags |= TextureSearchFlags.NoCreate;
 349              }
 350  
 351              Texture texture = FindOrCreateTexture(memoryManager, flags, info, 0, sizeHint: sizeHint);
 352  
 353              texture?.SynchronizeMemory();
 354  
 355              return texture;
 356          }
 357  
 358          /// <summary>
 359          /// Tries to find an existing texture, or create a new one if not found.
 360          /// </summary>
 361          /// <param name="memoryManager">GPU memory manager where the texture is mapped</param>
 362          /// <param name="formatInfo">Format of the texture</param>
 363          /// <param name="gpuAddress">GPU virtual address of the texture</param>
 364          /// <param name="xCount">Texture width in bytes</param>
 365          /// <param name="yCount">Texture height</param>
 366          /// <param name="stride">Texture stride if linear, otherwise ignored</param>
 367          /// <param name="isLinear">Indicates if the texture is linear or block linear</param>
 368          /// <param name="gobBlocksInY">GOB blocks in Y for block linear textures</param>
 369          /// <param name="gobBlocksInZ">GOB blocks in Z for 3D block linear textures</param>
 370          /// <returns>The texture</returns>
 371          public Texture FindOrCreateTexture(
 372              MemoryManager memoryManager,
 373              FormatInfo formatInfo,
 374              ulong gpuAddress,
 375              int xCount,
 376              int yCount,
 377              int stride,
 378              bool isLinear,
 379              int gobBlocksInY,
 380              int gobBlocksInZ)
 381          {
 382              TextureInfo info = new(
 383                  gpuAddress,
 384                  xCount / formatInfo.BytesPerPixel,
 385                  yCount,
 386                  1,
 387                  1,
 388                  1,
 389                  1,
 390                  stride,
 391                  isLinear,
 392                  gobBlocksInY,
 393                  gobBlocksInZ,
 394                  1,
 395                  Target.Texture2D,
 396                  formatInfo);
 397  
 398              Texture texture = FindOrCreateTexture(memoryManager, TextureSearchFlags.ForCopy, info, 0, sizeHint: new Size(xCount, yCount, 1));
 399  
 400              texture?.SynchronizeMemory();
 401  
 402              return texture;
 403          }
 404  
 405          /// <summary>
 406          /// Tries to find an existing texture, or create a new one if not found.
 407          /// </summary>
 408          /// <param name="memoryManager">GPU memory manager where the texture is mapped</param>
 409          /// <param name="colorState">Color buffer texture to find or create</param>
 410          /// <param name="layered">Indicates if the texture might be accessed with a non-zero layer index</param>
 411          /// <param name="discard">Indicates that the sizeHint region's data will be overwritten</param>
 412          /// <param name="samplesInX">Number of samples in the X direction, for MSAA</param>
 413          /// <param name="samplesInY">Number of samples in the Y direction, for MSAA</param>
 414          /// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
 415          /// <returns>The texture</returns>
 416          public Texture FindOrCreateTexture(
 417              MemoryManager memoryManager,
 418              RtColorState colorState,
 419              bool layered,
 420              bool discard,
 421              int samplesInX,
 422              int samplesInY,
 423              Size sizeHint)
 424          {
 425              bool isLinear = colorState.MemoryLayout.UnpackIsLinear();
 426  
 427              int gobBlocksInY = colorState.MemoryLayout.UnpackGobBlocksInY();
 428              int gobBlocksInZ = colorState.MemoryLayout.UnpackGobBlocksInZ();
 429  
 430              Target target;
 431  
 432              if (colorState.MemoryLayout.UnpackIsTarget3D())
 433              {
 434                  target = Target.Texture3D;
 435              }
 436              else if ((samplesInX | samplesInY) != 1)
 437              {
 438                  target = colorState.Depth > 1 && layered
 439                      ? Target.Texture2DMultisampleArray
 440                      : Target.Texture2DMultisample;
 441              }
 442              else
 443              {
 444                  target = colorState.Depth > 1 && layered
 445                      ? Target.Texture2DArray
 446                      : Target.Texture2D;
 447              }
 448  
 449              FormatInfo formatInfo = colorState.Format.Convert();
 450  
 451              int width, stride;
 452  
 453              // For linear textures, the width value is actually the stride.
 454              // We can easily get the width by dividing the stride by the bpp,
 455              // since the stride is the total number of bytes occupied by a
 456              // line. The stride should also meet alignment constraints however,
 457              // so the width we get here is the aligned width.
 458              if (isLinear)
 459              {
 460                  width = colorState.WidthOrStride / formatInfo.BytesPerPixel;
 461                  stride = colorState.WidthOrStride;
 462              }
 463              else
 464              {
 465                  width = colorState.WidthOrStride;
 466                  stride = 0;
 467              }
 468  
 469              TextureInfo info = new(
 470                  colorState.Address.Pack(),
 471                  GetMinimumWidthInGob(width, sizeHint.Width, formatInfo.BytesPerPixel, isLinear),
 472                  colorState.Height,
 473                  colorState.Depth,
 474                  1,
 475                  samplesInX,
 476                  samplesInY,
 477                  stride,
 478                  isLinear,
 479                  gobBlocksInY,
 480                  gobBlocksInZ,
 481                  1,
 482                  target,
 483                  formatInfo);
 484  
 485              int layerSize = !isLinear ? colorState.LayerSize * 4 : 0;
 486  
 487              var flags = TextureSearchFlags.WithUpscale;
 488  
 489              if (discard)
 490              {
 491                  flags |= TextureSearchFlags.DiscardData;
 492              }
 493  
 494              Texture texture = FindOrCreateTexture(memoryManager, flags, info, layerSize, sizeHint: sizeHint);
 495  
 496              texture?.SynchronizeMemory();
 497  
 498              return texture;
 499          }
 500  
 501          /// <summary>
 502          /// Tries to find an existing texture, or create a new one if not found.
 503          /// </summary>
 504          /// <param name="memoryManager">GPU memory manager where the texture is mapped</param>
 505          /// <param name="dsState">Depth-stencil buffer texture to find or create</param>
 506          /// <param name="size">Size of the depth-stencil texture</param>
 507          /// <param name="layered">Indicates if the texture might be accessed with a non-zero layer index</param>
 508          /// <param name="discard">Indicates that the sizeHint region's data will be overwritten</param>
 509          /// <param name="samplesInX">Number of samples in the X direction, for MSAA</param>
 510          /// <param name="samplesInY">Number of samples in the Y direction, for MSAA</param>
 511          /// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
 512          /// <returns>The texture</returns>
 513          public Texture FindOrCreateTexture(
 514              MemoryManager memoryManager,
 515              RtDepthStencilState dsState,
 516              Size3D size,
 517              bool layered,
 518              bool discard,
 519              int samplesInX,
 520              int samplesInY,
 521              Size sizeHint)
 522          {
 523              int gobBlocksInY = dsState.MemoryLayout.UnpackGobBlocksInY();
 524              int gobBlocksInZ = dsState.MemoryLayout.UnpackGobBlocksInZ();
 525  
 526              layered &= size.UnpackIsLayered();
 527  
 528              Target target;
 529  
 530              if ((samplesInX | samplesInY) != 1)
 531              {
 532                  target = size.Depth > 1 && layered
 533                      ? Target.Texture2DMultisampleArray
 534                      : Target.Texture2DMultisample;
 535              }
 536              else
 537              {
 538                  target = size.Depth > 1 && layered
 539                      ? Target.Texture2DArray
 540                      : Target.Texture2D;
 541              }
 542  
 543              FormatInfo formatInfo = dsState.Format.Convert();
 544  
 545              TextureInfo info = new(
 546                  dsState.Address.Pack(),
 547                  GetMinimumWidthInGob(size.Width, sizeHint.Width, formatInfo.BytesPerPixel, false),
 548                  size.Height,
 549                  size.Depth,
 550                  1,
 551                  samplesInX,
 552                  samplesInY,
 553                  0,
 554                  false,
 555                  gobBlocksInY,
 556                  gobBlocksInZ,
 557                  1,
 558                  target,
 559                  formatInfo);
 560  
 561              var flags = TextureSearchFlags.WithUpscale;
 562  
 563              if (discard)
 564              {
 565                  flags |= TextureSearchFlags.DiscardData;
 566              }
 567  
 568              Texture texture = FindOrCreateTexture(memoryManager, flags, info, dsState.LayerSize * 4, sizeHint: sizeHint);
 569  
 570              texture?.SynchronizeMemory();
 571  
 572              return texture;
 573          }
 574  
 575          /// <summary>
 576          /// For block linear textures, gets the minimum width of the texture
 577          /// that would still have the same number of GOBs per row as the original width.
 578          /// </summary>
 579          /// <param name="width">The possibly aligned texture width</param>
 580          /// <param name="minimumWidth">The minimum width that the texture may have without losing data</param>
 581          /// <param name="bytesPerPixel">Bytes per pixel of the texture format</param>
 582          /// <param name="isLinear">True if the texture is linear, false for block linear</param>
 583          /// <returns>The minimum width of the texture with the same amount of GOBs per row</returns>
 584          private static int GetMinimumWidthInGob(int width, int minimumWidth, int bytesPerPixel, bool isLinear)
 585          {
 586              if (isLinear || (uint)minimumWidth >= (uint)width)
 587              {
 588                  return width;
 589              }
 590  
 591              // Calculate the minimum possible that would not cause data loss
 592              // and would be still within the same GOB (aligned size would be the same).
 593              // This is useful for render and copy operations, where we don't know the
 594              // exact width of the texture, but it doesn't matter, as long the texture is
 595              // at least as large as the region being rendered or copied.
 596  
 597              int alignment = 64 / bytesPerPixel;
 598              int widthAligned = BitUtils.AlignUp(width, alignment);
 599  
 600              return Math.Clamp(widthAligned - alignment + 1, minimumWidth, widthAligned);
 601          }
 602  
 603          /// <summary>
 604          /// Determines if texture data should be fully discarded
 605          /// based on the size hint region and whether it is set to be discarded.
 606          /// </summary>
 607          /// <param name="discard">Whether the size hint region should be discarded</param>
 608          /// <param name="texture">The texture being discarded</param>
 609          /// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
 610          /// <returns>True if the data should be discarded, false otherwise</returns>
 611          private static bool ShouldDiscard(bool discard, Texture texture, Size? sizeHint)
 612          {
 613              return discard &&
 614                  texture.Info.DepthOrLayers == 1 &&
 615                  sizeHint != null &&
 616                  texture.Width <= sizeHint.Value.Width &&
 617                  texture.Height <= sizeHint.Value.Height;
 618          }
 619  
 620          /// <summary>
 621          /// Discards texture data if requested and possible.
 622          /// </summary>
 623          /// <param name="discard">Whether the size hint region should be discarded</param>
 624          /// <param name="texture">The texture being discarded</param>
 625          /// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
 626          private static void DiscardIfNeeded(bool discard, Texture texture, Size? sizeHint)
 627          {
 628              if (ShouldDiscard(discard, texture, sizeHint))
 629              {
 630                  texture.DiscardData();
 631              }
 632          }
 633  
 634          /// <summary>
 635          /// Tries to find an existing texture, or create a new one if not found.
 636          /// </summary>
 637          /// <param name="memoryManager">GPU memory manager where the texture is mapped</param>
 638          /// <param name="flags">The texture search flags, defines texture comparison rules</param>
 639          /// <param name="info">Texture information of the texture to be found or created</param>
 640          /// <param name="layerSize">Size in bytes of a single texture layer</param>
 641          /// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
 642          /// <param name="range">Optional ranges of physical memory where the texture data is located</param>
 643          /// <returns>The texture</returns>
 644          public Texture FindOrCreateTexture(
 645              MemoryManager memoryManager,
 646              TextureSearchFlags flags,
 647              TextureInfo info,
 648              int layerSize = 0,
 649              Size? sizeHint = null,
 650              MultiRange? range = null)
 651          {
 652              bool isSamplerTexture = (flags & TextureSearchFlags.ForSampler) != 0;
 653              bool discard = (flags & TextureSearchFlags.DiscardData) != 0;
 654  
 655              TextureScaleMode scaleMode = IsUpscaleCompatible(info, (flags & TextureSearchFlags.WithUpscale) != 0);
 656  
 657              ulong address;
 658  
 659              if (range != null)
 660              {
 661                  address = range.Value.GetSubRange(0).Address;
 662              }
 663              else
 664              {
 665                  address = memoryManager.Translate(info.GpuAddress);
 666  
 667                  // If the start address is unmapped, let's try to find a page of memory that is mapped.
 668                  if (address == MemoryManager.PteUnmapped)
 669                  {
 670                      // Make sure that the dimensions are valid before calculating the texture size.
 671                      if (info.Width < 1 || info.Height < 1 || info.Levels < 1)
 672                      {
 673                          return null;
 674                      }
 675  
 676                      if ((info.Target == Target.Texture3D ||
 677                           info.Target == Target.Texture2DArray ||
 678                           info.Target == Target.Texture2DMultisampleArray ||
 679                           info.Target == Target.CubemapArray) && info.DepthOrLayers < 1)
 680                      {
 681                          return null;
 682                      }
 683  
 684                      ulong dataSize = (ulong)info.CalculateSizeInfo(layerSize).TotalSize;
 685  
 686                      address = memoryManager.TranslateFirstMapped(info.GpuAddress, dataSize);
 687                  }
 688  
 689                  // If address is still invalid, the texture is fully unmapped, so it has no data, just return null.
 690                  if (address == MemoryManager.PteUnmapped)
 691                  {
 692                      return null;
 693                  }
 694              }
 695  
 696              int sameAddressOverlapsCount;
 697  
 698              _texturesLock.EnterReadLock();
 699  
 700              try
 701              {
 702                  // Try to find a perfect texture match, with the same address and parameters.
 703                  sameAddressOverlapsCount = _textures.FindOverlaps(address, ref _textureOverlaps);
 704              }
 705              finally
 706              {
 707                  _texturesLock.ExitReadLock();
 708              }
 709  
 710              Texture texture = null;
 711  
 712              long bestSequence = 0;
 713  
 714              for (int index = 0; index < sameAddressOverlapsCount; index++)
 715              {
 716                  Texture overlap = _textureOverlaps[index];
 717  
 718                  TextureMatchQuality matchQuality = overlap.IsExactMatch(info, flags);
 719  
 720                  if (matchQuality != TextureMatchQuality.NoMatch)
 721                  {
 722                      // If the parameters match, we need to make sure the texture is mapped to the same memory regions.
 723                      if (range != null)
 724                      {
 725                          // If a range of memory was supplied, just check if the ranges match.
 726                          if (!overlap.Range.Equals(range.Value))
 727                          {
 728                              continue;
 729                          }
 730                      }
 731                      else
 732                      {
 733                          // If no range was supplied, we can check if the GPU virtual address match. If they do,
 734                          // we know the textures are located at the same memory region.
 735                          // If they don't, it may still be mapped to the same physical region, so we
 736                          // do a more expensive check to tell if they are mapped into the same physical regions.
 737                          // If the GPU VA for the texture has ever been unmapped, then the range must be checked regardless.
 738                          if ((overlap.Info.GpuAddress != info.GpuAddress || overlap.ChangedMapping) &&
 739                              !memoryManager.CompareRange(overlap.Range, info.GpuAddress))
 740                          {
 741                              continue;
 742                          }
 743                      }
 744  
 745                      if (texture == null || overlap.Group.ModifiedSequence - bestSequence > 0)
 746                      {
 747                          texture = overlap;
 748                          bestSequence = overlap.Group.ModifiedSequence;
 749                      }
 750                  }
 751              }
 752  
 753              if (texture != null)
 754              {
 755                  DiscardIfNeeded(discard, texture, sizeHint);
 756  
 757                  texture.SynchronizeMemory();
 758  
 759                  return texture;
 760              }
 761              else if (flags.HasFlag(TextureSearchFlags.NoCreate))
 762              {
 763                  return null;
 764              }
 765  
 766              // Calculate texture sizes, used to find all overlapping textures.
 767              SizeInfo sizeInfo = info.CalculateSizeInfo(layerSize);
 768  
 769              ulong size = (ulong)sizeInfo.TotalSize;
 770              bool partiallyMapped = false;
 771  
 772              if (range == null)
 773              {
 774                  range = memoryManager.GetPhysicalRegions(info.GpuAddress, size);
 775  
 776                  for (int i = 0; i < range.Value.Count; i++)
 777                  {
 778                      if (range.Value.GetSubRange(i).Address == MemoryManager.PteUnmapped)
 779                      {
 780                          partiallyMapped = true;
 781                          break;
 782                      }
 783                  }
 784              }
 785  
 786              // Find view compatible matches.
 787              int overlapsCount = 0;
 788  
 789              if (info.Target != Target.TextureBuffer)
 790              {
 791                  _texturesLock.EnterReadLock();
 792  
 793                  try
 794                  {
 795                      overlapsCount = _textures.FindOverlaps(range.Value, ref _textureOverlaps);
 796                  }
 797                  finally
 798                  {
 799                      _texturesLock.ExitReadLock();
 800                  }
 801              }
 802  
 803              if (_overlapInfo.Length != _textureOverlaps.Length)
 804              {
 805                  Array.Resize(ref _overlapInfo, _textureOverlaps.Length);
 806              }
 807  
 808              // =============== Find Texture View of Existing Texture ===============
 809  
 810              int fullyCompatible = 0;
 811  
 812              // Evaluate compatibility of overlaps, add temporary references
 813              int preferredOverlap = -1;
 814  
 815              for (int index = 0; index < overlapsCount; index++)
 816              {
 817                  Texture overlap = _textureOverlaps[index];
 818                  TextureViewCompatibility overlapCompatibility = overlap.IsViewCompatible(
 819                      info,
 820                      range.Value,
 821                      isSamplerTexture,
 822                      sizeInfo.LayerSize,
 823                      _context.Capabilities,
 824                      out int firstLayer,
 825                      out int firstLevel,
 826                      flags);
 827  
 828                  if (overlapCompatibility >= TextureViewCompatibility.FormatAlias)
 829                  {
 830                      if (overlap.IsView)
 831                      {
 832                          overlapCompatibility = TextureViewCompatibility.CopyOnly;
 833                      }
 834                      else
 835                      {
 836                          fullyCompatible++;
 837  
 838                          if (preferredOverlap == -1 || overlap.Group.ModifiedSequence - bestSequence > 0)
 839                          {
 840                              preferredOverlap = index;
 841                              bestSequence = overlap.Group.ModifiedSequence;
 842                          }
 843                      }
 844                  }
 845  
 846                  _overlapInfo[index] = new OverlapInfo(overlapCompatibility, firstLayer, firstLevel);
 847                  overlap.IncrementReferenceCount();
 848              }
 849  
 850              // Search through the overlaps to find a compatible view and establish any copy dependencies.
 851  
 852              if (preferredOverlap != -1)
 853              {
 854                  Texture overlap = _textureOverlaps[preferredOverlap];
 855                  OverlapInfo oInfo = _overlapInfo[preferredOverlap];
 856  
 857                  bool aliased = oInfo.Compatibility == TextureViewCompatibility.FormatAlias;
 858  
 859                  if (!isSamplerTexture)
 860                  {
 861                      // If this is not a sampler texture, the size might be different from the requested size,
 862                      // so we need to make sure the texture information has the correct size for this base texture,
 863                      // before creating the view.
 864  
 865                      info = info.CreateInfoForLevelView(overlap, oInfo.FirstLevel, aliased);
 866                  }
 867                  else if (aliased)
 868                  {
 869                      // The format must be changed to match the parent.
 870                      info = info.CreateInfoWithFormat(overlap.Info.FormatInfo);
 871                  }
 872  
 873                  texture = overlap.CreateView(info, sizeInfo, range.Value, oInfo.FirstLayer, oInfo.FirstLevel);
 874                  texture.SynchronizeMemory();
 875              }
 876              else
 877              {
 878                  for (int index = 0; index < overlapsCount; index++)
 879                  {
 880                      Texture overlap = _textureOverlaps[index];
 881                      OverlapInfo oInfo = _overlapInfo[index];
 882  
 883                      if (oInfo.Compatibility == TextureViewCompatibility.CopyOnly && fullyCompatible == 0)
 884                      {
 885                          // Only copy compatible. If there's another choice for a FULLY compatible texture, choose that instead.
 886  
 887                          texture = new Texture(_context, _physicalMemory, info, sizeInfo, range.Value, scaleMode);
 888  
 889                          // If the new texture is larger than the existing one, we need to fill the remaining space with CPU data,
 890                          // otherwise we only need the data that is copied from the existing texture, without loading the CPU data.
 891                          bool updateNewTexture = texture.Width > overlap.Width || texture.Height > overlap.Height;
 892  
 893                          texture.InitializeGroup(true, true, new List<TextureIncompatibleOverlap>());
 894                          texture.InitializeData(false, updateNewTexture);
 895  
 896                          overlap.SynchronizeMemory();
 897                          overlap.CreateCopyDependency(texture, oInfo.FirstLayer, oInfo.FirstLevel, true);
 898                          break;
 899                      }
 900                  }
 901              }
 902  
 903              if (texture != null)
 904              {
 905                  // This texture could be a view of multiple parent textures with different storages, even if it is a view.
 906                  // When a texture is created, make sure all possible dependencies to other textures are created as copies.
 907                  // (even if it could be fulfilled without a copy)
 908  
 909                  for (int index = 0; index < overlapsCount; index++)
 910                  {
 911                      Texture overlap = _textureOverlaps[index];
 912                      OverlapInfo oInfo = _overlapInfo[index];
 913  
 914                      if (oInfo.Compatibility <= TextureViewCompatibility.LayoutIncompatible)
 915                      {
 916                          if (!overlap.IsView && texture.DataOverlaps(overlap, oInfo.Compatibility))
 917                          {
 918                              texture.Group.RegisterIncompatibleOverlap(new TextureIncompatibleOverlap(overlap.Group, oInfo.Compatibility), true);
 919                          }
 920                      }
 921                      else if (overlap.Group != texture.Group)
 922                      {
 923                          overlap.SynchronizeMemory();
 924                          overlap.CreateCopyDependency(texture, oInfo.FirstLayer, oInfo.FirstLevel, true);
 925                      }
 926                  }
 927  
 928                  texture.SynchronizeMemory();
 929              }
 930  
 931              // =============== Create a New Texture ===============
 932  
 933              // No match, create a new texture.
 934              if (texture == null)
 935              {
 936                  texture = new Texture(_context, _physicalMemory, info, sizeInfo, range.Value, scaleMode);
 937  
 938                  // Step 1: Find textures that are view compatible with the new texture.
 939                  // Any textures that are incompatible will contain garbage data, so they should be removed where possible.
 940  
 941                  int viewCompatible = 0;
 942                  fullyCompatible = 0;
 943                  bool setData = isSamplerTexture || overlapsCount == 0 || flags.HasFlag(TextureSearchFlags.ForCopy);
 944  
 945                  bool hasLayerViews = false;
 946                  bool hasMipViews = false;
 947  
 948                  var incompatibleOverlaps = new List<TextureIncompatibleOverlap>();
 949  
 950                  for (int index = 0; index < overlapsCount; index++)
 951                  {
 952                      Texture overlap = _textureOverlaps[index];
 953                      bool overlapInCache = overlap.CacheNode != null;
 954  
 955                      TextureViewCompatibility compatibility = texture.IsViewCompatible(
 956                          overlap.Info,
 957                          overlap.Range,
 958                          exactSize: true,
 959                          overlap.LayerSize,
 960                          _context.Capabilities,
 961                          out int firstLayer,
 962                          out int firstLevel);
 963  
 964                      if (overlap.IsView && compatibility == TextureViewCompatibility.Full)
 965                      {
 966                          compatibility = TextureViewCompatibility.CopyOnly;
 967                      }
 968  
 969                      if (compatibility > TextureViewCompatibility.LayoutIncompatible)
 970                      {
 971                          _overlapInfo[viewCompatible] = new OverlapInfo(compatibility, firstLayer, firstLevel);
 972                          _textureOverlaps[index] = _textureOverlaps[viewCompatible];
 973                          _textureOverlaps[viewCompatible] = overlap;
 974  
 975                          if (compatibility == TextureViewCompatibility.Full)
 976                          {
 977                              if (viewCompatible != fullyCompatible)
 978                              {
 979                                  // Swap overlaps so that the fully compatible views have priority.
 980  
 981                                  _overlapInfo[viewCompatible] = _overlapInfo[fullyCompatible];
 982                                  _textureOverlaps[viewCompatible] = _textureOverlaps[fullyCompatible];
 983  
 984                                  _overlapInfo[fullyCompatible] = new OverlapInfo(compatibility, firstLayer, firstLevel);
 985                                  _textureOverlaps[fullyCompatible] = overlap;
 986                              }
 987  
 988                              fullyCompatible++;
 989                          }
 990  
 991                          viewCompatible++;
 992  
 993                          hasLayerViews |= overlap.Info.GetSlices() < texture.Info.GetSlices();
 994                          hasMipViews |= overlap.Info.Levels < texture.Info.Levels;
 995                      }
 996                      else
 997                      {
 998                          bool dataOverlaps = texture.DataOverlaps(overlap, compatibility);
 999  
1000                          if (!overlap.IsView && dataOverlaps && !incompatibleOverlaps.Exists(incompatible => incompatible.Group == overlap.Group))
1001                          {
1002                              incompatibleOverlaps.Add(new TextureIncompatibleOverlap(overlap.Group, compatibility));
1003                          }
1004  
1005                          bool removeOverlap;
1006                          bool modified = overlap.CheckModified(false);
1007  
1008                          if (overlapInCache || !setData)
1009                          {
1010                              if (!dataOverlaps)
1011                              {
1012                                  // Allow textures to overlap if their data does not actually overlap.
1013                                  // This typically happens when mip level subranges of a layered texture are used. (each texture fills the gaps of the others)
1014                                  continue;
1015                              }
1016  
1017                              // The overlap texture is going to contain garbage data after we draw, or is generally incompatible.
1018                              // The texture group will obtain copy dependencies for any subresources that are compatible between the two textures,
1019                              // but sometimes its data must be flushed regardless.
1020  
1021                              // If the texture was modified since its last use, then that data is probably meant to go into this texture.
1022                              // If the data has been modified by the CPU, then it also shouldn't be flushed.
1023  
1024                              bool flush = overlapInCache && !modified && overlap.AlwaysFlushOnOverlap;
1025  
1026                              setData |= modified || flush;
1027  
1028                              if (overlapInCache)
1029                              {
1030                                  if (flush || overlap.HadPoolOwner || overlap.IsView)
1031                                  {
1032                                      _cache.Remove(overlap, flush);
1033                                  }
1034                                  else
1035                                  {
1036                                      // This texture has only ever been referenced in the AutoDeleteCache.
1037                                      // Keep this texture alive with the short duration cache, as it may be used often but not sampled.
1038  
1039                                      _cache.AddShortCache(overlap);
1040                                  }
1041                              }
1042  
1043                              removeOverlap = modified;
1044                          }
1045                          else
1046                          {
1047                              // If an incompatible overlapping texture has been modified, then it's data is likely destined for this texture,
1048                              // and the overlapped texture will contain garbage. In this case, it should be removed to save memory.
1049                              removeOverlap = modified;
1050                          }
1051  
1052                          if (removeOverlap && overlap.Info.Target != Target.TextureBuffer)
1053                          {
1054                              overlap.RemoveFromPools(false);
1055                          }
1056                      }
1057                  }
1058  
1059                  texture.InitializeGroup(hasLayerViews, hasMipViews, incompatibleOverlaps);
1060  
1061                  // We need to synchronize before copying the old view data to the texture,
1062                  // otherwise the copied data would be overwritten by a future synchronization.
1063                  texture.InitializeData(false, setData && !ShouldDiscard(discard, texture, sizeHint));
1064  
1065                  texture.Group.InitializeOverlaps();
1066  
1067                  for (int index = 0; index < viewCompatible; index++)
1068                  {
1069                      Texture overlap = _textureOverlaps[index];
1070  
1071                      OverlapInfo oInfo = _overlapInfo[index];
1072  
1073                      if (overlap.Group == texture.Group)
1074                      {
1075                          // If the texture group is equal, then this texture (or its parent) is already a view.
1076                          continue;
1077                      }
1078  
1079                      // Note: If we allow different sizes for those overlaps,
1080                      // we need to make sure that the "info" has the correct size for the parent texture here.
1081                      // Since this is not allowed right now, we don't need to do it.
1082  
1083                      TextureInfo overlapInfo = overlap.Info;
1084  
1085                      if (texture.ScaleFactor != overlap.ScaleFactor)
1086                      {
1087                          // A bit tricky, our new texture may need to contain an existing texture that is upscaled, but isn't itself.
1088                          // In that case, we prefer the higher scale only if our format is render-target-like, otherwise we scale the view down before copy.
1089  
1090                          texture.PropagateScale(overlap);
1091                      }
1092  
1093                      if (oInfo.Compatibility != TextureViewCompatibility.Full)
1094                      {
1095                          // Copy only compatibility, or target texture is already a view.
1096  
1097                          overlap.SynchronizeMemory();
1098                          texture.CreateCopyDependency(overlap, oInfo.FirstLayer, oInfo.FirstLevel, false);
1099                      }
1100                      else
1101                      {
1102                          TextureCreateInfo createInfo = GetCreateInfo(overlapInfo, _context.Capabilities, overlap.ScaleFactor);
1103  
1104                          ITexture newView = texture.HostTexture.CreateView(createInfo, oInfo.FirstLayer, oInfo.FirstLevel);
1105  
1106                          overlap.SynchronizeMemory();
1107  
1108                          overlap.HostTexture.CopyTo(newView, 0, 0);
1109  
1110                          overlap.ReplaceView(texture, overlapInfo, newView, oInfo.FirstLayer, oInfo.FirstLevel);
1111                      }
1112                  }
1113  
1114                  texture.SynchronizeMemory();
1115              }
1116  
1117              // Sampler textures are managed by the texture pool, all other textures
1118              // are managed by the auto delete cache.
1119              if (!isSamplerTexture)
1120              {
1121                  _cache.Add(texture);
1122              }
1123  
1124              _texturesLock.EnterWriteLock();
1125  
1126              try
1127              {
1128                  _textures.Add(texture);
1129              }
1130              finally
1131              {
1132                  _texturesLock.ExitWriteLock();
1133              }
1134  
1135              if (partiallyMapped)
1136              {
1137                  lock (_partiallyMappedTextures)
1138                  {
1139                      _partiallyMappedTextures.Add(texture);
1140                  }
1141              }
1142  
1143              ShrinkOverlapsBufferIfNeeded();
1144  
1145              for (int i = 0; i < overlapsCount; i++)
1146              {
1147                  _textureOverlaps[i].DecrementReferenceCount();
1148              }
1149  
1150              return texture;
1151          }
1152  
1153          /// <summary>
1154          /// Attempt to find a texture on the short duration cache.
1155          /// </summary>
1156          /// <param name="descriptor">The texture descriptor</param>
1157          /// <returns>The texture if found, null otherwise</returns>
1158          public Texture FindShortCache(in TextureDescriptor descriptor)
1159          {
1160              return _cache.FindShortCache(descriptor);
1161          }
1162  
1163          /// <summary>
1164          /// Tries to find an existing texture matching the given buffer copy destination. If none is found, returns null.
1165          /// </summary>
1166          /// <param name="memoryManager">GPU memory manager where the texture is mapped</param>
1167          /// <param name="gpuVa">GPU virtual address of the texture</param>
1168          /// <param name="bpp">Bytes per pixel</param>
1169          /// <param name="stride">If <paramref name="linear"/> is true, should have the texture stride, otherwise ignored</param>
1170          /// <param name="height">If <paramref name="linear"/> is false, should have the texture height, otherwise ignored</param>
1171          /// <param name="xCount">Number of pixels to be copied per line</param>
1172          /// <param name="yCount">Number of lines to be copied</param>
1173          /// <param name="linear">True if the texture has a linear layout, false otherwise</param>
1174          /// <param name="gobBlocksInY">If <paramref name="linear"/> is false, the amount of GOB blocks in the Y axis</param>
1175          /// <param name="gobBlocksInZ">If <paramref name="linear"/> is false, the amount of GOB blocks in the Z axis</param>
1176          /// <returns>A matching texture, or null if there is no match</returns>
1177          public Texture FindTexture(
1178              MemoryManager memoryManager,
1179              ulong gpuVa,
1180              int bpp,
1181              int stride,
1182              int height,
1183              int xCount,
1184              int yCount,
1185              bool linear,
1186              int gobBlocksInY,
1187              int gobBlocksInZ)
1188          {
1189              ulong address = memoryManager.Translate(gpuVa);
1190  
1191              if (address == MemoryManager.PteUnmapped)
1192              {
1193                  return null;
1194              }
1195  
1196              int addressMatches;
1197  
1198              _texturesLock.EnterReadLock();
1199  
1200              try
1201              {
1202                  addressMatches = _textures.FindOverlaps(address, ref _textureOverlaps);
1203              }
1204              finally
1205              {
1206                  _texturesLock.ExitReadLock();
1207              }
1208  
1209              Texture textureMatch = null;
1210  
1211              for (int i = 0; i < addressMatches; i++)
1212              {
1213                  Texture texture = _textureOverlaps[i];
1214                  FormatInfo format = texture.Info.FormatInfo;
1215  
1216                  if (texture.Info.DepthOrLayers > 1 || texture.Info.Levels > 1 || texture.Info.FormatInfo.IsCompressed)
1217                  {
1218                      // Don't support direct buffer copies to anything that isn't a single 2D image, uncompressed.
1219                      continue;
1220                  }
1221  
1222                  bool match;
1223  
1224                  if (linear)
1225                  {
1226                      // Size is not available for linear textures. Use the stride and end of the copy region instead.
1227  
1228                      match = texture.Info.IsLinear && texture.Info.Stride == stride && yCount == texture.Info.Height;
1229                  }
1230                  else
1231                  {
1232                      // Bpp may be a mismatch between the target texture and the param.
1233                      // Due to the way linear strided and block layouts work, widths can be multiplied by Bpp for comparison.
1234                      // Note: tex.Width is the aligned texture size. Prefer param.XCount, as the destination should be a texture with that exact size.
1235  
1236                      bool sizeMatch = xCount * bpp == texture.Info.Width * format.BytesPerPixel && height == texture.Info.Height;
1237                      bool formatMatch = !texture.Info.IsLinear &&
1238                                          texture.Info.GobBlocksInY == gobBlocksInY &&
1239                                          texture.Info.GobBlocksInZ == gobBlocksInZ;
1240  
1241                      match = sizeMatch && formatMatch;
1242                  }
1243  
1244                  if (match)
1245                  {
1246                      if (textureMatch == null)
1247                      {
1248                          textureMatch = texture;
1249                      }
1250                      else if (texture.Group != textureMatch.Group)
1251                      {
1252                          return null; // It's ambiguous which texture should match between multiple choices, so leave it up to the slow path.
1253                      }
1254                  }
1255              }
1256  
1257              return textureMatch;
1258          }
1259  
1260          /// <summary>
1261          /// Resizes the temporary buffer used for range list intersection results, if it has grown too much.
1262          /// </summary>
1263          private void ShrinkOverlapsBufferIfNeeded()
1264          {
1265              if (_textureOverlaps.Length > OverlapsBufferMaxCapacity)
1266              {
1267                  Array.Resize(ref _textureOverlaps, OverlapsBufferMaxCapacity);
1268              }
1269          }
1270  
1271          /// <summary>
1272          /// Gets a texture creation information from texture information.
1273          /// This can be used to create new host textures.
1274          /// </summary>
1275          /// <param name="info">Texture information</param>
1276          /// <param name="caps">GPU capabilities</param>
1277          /// <param name="scale">Texture scale factor, to be applied to the texture size</param>
1278          /// <returns>The texture creation information</returns>
1279          public static TextureCreateInfo GetCreateInfo(TextureInfo info, Capabilities caps, float scale)
1280          {
1281              FormatInfo formatInfo = TextureCompatibility.ToHostCompatibleFormat(info, caps);
1282  
1283              if (info.Target == Target.TextureBuffer && !caps.SupportsSnormBufferTextureFormat)
1284              {
1285                  // If the host does not support signed normalized formats, we use a signed integer format instead.
1286                  // The shader will need the appropriate conversion code to compensate.
1287                  switch (formatInfo.Format)
1288                  {
1289                      case Format.R8Snorm:
1290                          formatInfo = new FormatInfo(Format.R8Sint, 1, 1, 1, 1);
1291                          break;
1292                      case Format.R16Snorm:
1293                          formatInfo = new FormatInfo(Format.R16Sint, 1, 1, 2, 1);
1294                          break;
1295                      case Format.R8G8Snorm:
1296                          formatInfo = new FormatInfo(Format.R8G8Sint, 1, 1, 2, 2);
1297                          break;
1298                      case Format.R16G16Snorm:
1299                          formatInfo = new FormatInfo(Format.R16G16Sint, 1, 1, 4, 2);
1300                          break;
1301                      case Format.R8G8B8A8Snorm:
1302                          formatInfo = new FormatInfo(Format.R8G8B8A8Sint, 1, 1, 4, 4);
1303                          break;
1304                      case Format.R16G16B16A16Snorm:
1305                          formatInfo = new FormatInfo(Format.R16G16B16A16Sint, 1, 1, 8, 4);
1306                          break;
1307                  }
1308              }
1309  
1310              int width = info.Width / info.SamplesInX;
1311              int height = info.Height / info.SamplesInY;
1312  
1313              int depth = info.GetDepth() * info.GetLayers();
1314  
1315              if (scale != 1f)
1316              {
1317                  width = (int)MathF.Ceiling(width * scale);
1318                  height = (int)MathF.Ceiling(height * scale);
1319              }
1320  
1321              return new TextureCreateInfo(
1322                  width,
1323                  height,
1324                  depth,
1325                  info.Levels,
1326                  info.Samples,
1327                  formatInfo.BlockWidth,
1328                  formatInfo.BlockHeight,
1329                  formatInfo.BytesPerPixel,
1330                  formatInfo.Format,
1331                  info.DepthStencilMode,
1332                  info.Target,
1333                  info.SwizzleR,
1334                  info.SwizzleG,
1335                  info.SwizzleB,
1336                  info.SwizzleA);
1337          }
1338  
1339          /// <summary>
1340          /// Removes a texture from the cache.
1341          /// </summary>
1342          /// <remarks>
1343          /// This only removes the texture from the internal list, not from the auto-deletion cache.
1344          /// It may still have live references after the removal.
1345          /// </remarks>
1346          /// <param name="texture">The texture to be removed</param>
1347          public void RemoveTextureFromCache(Texture texture)
1348          {
1349              _texturesLock.EnterWriteLock();
1350  
1351              try
1352              {
1353                  _textures.Remove(texture);
1354              }
1355              finally
1356              {
1357                  _texturesLock.ExitWriteLock();
1358              }
1359  
1360              lock (_partiallyMappedTextures)
1361              {
1362                  _partiallyMappedTextures.Remove(texture);
1363              }
1364          }
1365  
1366          /// <summary>
1367          /// Queries a texture's memory range and marks it as partially mapped or not.
1368          /// Partially mapped textures re-evaluate their memory range after each time GPU memory is mapped.
1369          /// </summary>
1370          /// <param name="memoryManager">GPU memory manager where the texture is mapped</param>
1371          /// <param name="address">The virtual address of the texture</param>
1372          /// <param name="texture">The texture to be marked</param>
1373          /// <returns>The physical regions for the texture, found when evaluating whether the texture was partially mapped</returns>
1374          public MultiRange UpdatePartiallyMapped(MemoryManager memoryManager, ulong address, Texture texture)
1375          {
1376              MultiRange range;
1377              lock (_partiallyMappedTextures)
1378              {
1379                  range = memoryManager.GetPhysicalRegions(address, texture.Size);
1380                  bool partiallyMapped = false;
1381  
1382                  for (int i = 0; i < range.Count; i++)
1383                  {
1384                      if (range.GetSubRange(i).Address == MemoryManager.PteUnmapped)
1385                      {
1386                          partiallyMapped = true;
1387                          break;
1388                      }
1389                  }
1390  
1391                  if (partiallyMapped)
1392                  {
1393                      _partiallyMappedTextures.Add(texture);
1394                  }
1395                  else
1396                  {
1397                      _partiallyMappedTextures.Remove(texture);
1398                  }
1399              }
1400  
1401              return range;
1402          }
1403  
1404          /// <summary>
1405          /// Adds a texture to the short duration cache. This typically keeps it alive for two ticks.
1406          /// </summary>
1407          /// <param name="texture">Texture to add to the short cache</param>
1408          /// <param name="descriptor">Last used texture descriptor</param>
1409          public void AddShortCache(Texture texture, ref TextureDescriptor descriptor)
1410          {
1411              _cache.AddShortCache(texture, ref descriptor);
1412          }
1413  
1414          /// <summary>
1415          /// Adds a texture to the short duration cache without a descriptor. This typically keeps it alive for two ticks.
1416          /// On expiry, it will be removed from the AutoDeleteCache.
1417          /// </summary>
1418          /// <param name="texture">Texture to add to the short cache</param>
1419          public void AddShortCache(Texture texture)
1420          {
1421              _cache.AddShortCache(texture);
1422          }
1423  
1424          /// <summary>
1425          /// Removes a texture from the short duration cache.
1426          /// </summary>
1427          /// <param name="texture">Texture to remove from the short cache</param>
1428          public void RemoveShortCache(Texture texture)
1429          {
1430              _cache.RemoveShortCache(texture);
1431          }
1432  
1433          /// <summary>
1434          /// Ticks periodic elements of the texture cache.
1435          /// </summary>
1436          public void Tick()
1437          {
1438              _cache.ProcessShortCache();
1439          }
1440  
1441          /// <summary>
1442          /// Disposes all textures and samplers in the cache.
1443          /// It's an error to use the texture cache after disposal.
1444          /// </summary>
1445          public void Dispose()
1446          {
1447              _texturesLock.EnterReadLock();
1448  
1449              try
1450              {
1451                  foreach (Texture texture in _textures)
1452                  {
1453                      texture.Dispose();
1454                  }
1455              }
1456              finally
1457              {
1458                  _texturesLock.ExitReadLock();
1459              }
1460          }
1461      }
1462  }