/ src / Ryujinx.Graphics.Gpu / Image / TexturePool.cs
TexturePool.cs
  1  using Ryujinx.Common.Logging;
  2  using Ryujinx.Graphics.GAL;
  3  using Ryujinx.Graphics.Gpu.Memory;
  4  using Ryujinx.Graphics.Texture;
  5  using Ryujinx.Memory.Range;
  6  using System;
  7  using System.Collections.Concurrent;
  8  using System.Collections.Generic;
  9  using System.Numerics;
 10  using System.Threading;
 11  
 12  namespace Ryujinx.Graphics.Gpu.Image
 13  {
 14      /// <summary>
 15      /// Texture pool.
 16      /// </summary>
 17      class TexturePool : Pool<Texture, TextureDescriptor>, IPool<TexturePool>
 18      {
 19          /// <summary>
 20          /// A request to dereference a texture from a pool.
 21          /// </summary>
 22          private readonly struct DereferenceRequest
 23          {
 24              /// <summary>
 25              /// Whether the dereference is due to a mapping change or not.
 26              /// </summary>
 27              public readonly bool IsRemapped;
 28  
 29              /// <summary>
 30              /// The texture being dereferenced.
 31              /// </summary>
 32              public readonly Texture Texture;
 33  
 34              /// <summary>
 35              /// The ID of the pool entry this reference belonged to.
 36              /// </summary>
 37              public readonly int ID;
 38  
 39              /// <summary>
 40              /// Create a dereference request for a texture with a specific pool ID, and remapped flag.
 41              /// </summary>
 42              /// <param name="isRemapped">Whether the dereference is due to a mapping change or not</param>
 43              /// <param name="texture">The texture being dereferenced</param>
 44              /// <param name="id">The ID of the pool entry, used to restore remapped textures</param>
 45              private DereferenceRequest(bool isRemapped, Texture texture, int id)
 46              {
 47                  IsRemapped = isRemapped;
 48                  Texture = texture;
 49                  ID = id;
 50              }
 51  
 52              /// <summary>
 53              /// Create a dereference request for a texture removal.
 54              /// </summary>
 55              /// <param name="texture">The texture being removed</param>
 56              /// <returns>A texture removal dereference request</returns>
 57              public static DereferenceRequest Remove(Texture texture)
 58              {
 59                  return new DereferenceRequest(false, texture, 0);
 60              }
 61  
 62              /// <summary>
 63              /// Create a dereference request for a texture remapping with a specific pool ID.
 64              /// </summary>
 65              /// <param name="texture">The texture being remapped</param>
 66              /// <param name="id">The ID of the pool entry, used to restore remapped textures</param>
 67              /// <returns>A remap dereference request</returns>
 68              public static DereferenceRequest Remap(Texture texture, int id)
 69              {
 70                  return new DereferenceRequest(true, texture, id);
 71              }
 72          }
 73  
 74          private readonly GpuChannel _channel;
 75          private readonly ConcurrentQueue<DereferenceRequest> _dereferenceQueue = new();
 76          private TextureDescriptor _defaultDescriptor;
 77  
 78          /// <summary>
 79          /// List of textures that shares the same memory region, but have different formats.
 80          /// </summary>
 81          private class TextureAliasList
 82          {
 83              /// <summary>
 84              /// Alias texture.
 85              /// </summary>
 86              /// <param name="Format">Texture format</param>
 87              /// <param name="Texture">Texture</param>
 88              private readonly record struct Alias(Format Format, Texture Texture);
 89  
 90              /// <summary>
 91              /// List of texture aliases.
 92              /// </summary>
 93              private readonly List<Alias> _aliases;
 94  
 95              /// <summary>
 96              /// Creates a new instance of the texture alias list.
 97              /// </summary>
 98              public TextureAliasList()
 99              {
100                  _aliases = new List<Alias>();
101              }
102  
103              /// <summary>
104              /// Adds a new texture alias.
105              /// </summary>
106              /// <param name="format">Alias format</param>
107              /// <param name="texture">Alias texture</param>
108              public void Add(Format format, Texture texture)
109              {
110                  _aliases.Add(new Alias(format, texture));
111                  texture.IncrementReferenceCount();
112              }
113  
114              /// <summary>
115              /// Finds a texture with the requested format, or returns null if not found.
116              /// </summary>
117              /// <param name="format">Format to find</param>
118              /// <returns>Texture with the requested format, or null if not found</returns>
119              public Texture Find(Format format)
120              {
121                  foreach (var alias in _aliases)
122                  {
123                      if (alias.Format == format)
124                      {
125                          return alias.Texture;
126                      }
127                  }
128  
129                  return null;
130              }
131  
132              /// <summary>
133              /// Removes all alias textures.
134              /// </summary>
135              public void Destroy()
136              {
137                  foreach (var entry in _aliases)
138                  {
139                      entry.Texture.DecrementReferenceCount();
140                  }
141  
142                  _aliases.Clear();
143              }
144          }
145  
146          private readonly Dictionary<Texture, TextureAliasList> _aliasLists;
147  
148          /// <summary>
149          /// Linked list node used on the texture pool cache.
150          /// </summary>
151          public LinkedListNode<TexturePool> CacheNode { get; set; }
152  
153          /// <summary>
154          /// Timestamp used by the texture pool cache, updated on every use of this texture pool.
155          /// </summary>
156          public ulong CacheTimestamp { get; set; }
157  
158          /// <summary>
159          /// Creates a new instance of the texture pool.
160          /// </summary>
161          /// <param name="context">GPU context that the texture pool belongs to</param>
162          /// <param name="channel">GPU channel that the texture pool belongs to</param>
163          /// <param name="address">Address of the texture pool in guest memory</param>
164          /// <param name="maximumId">Maximum texture ID of the texture pool (equal to maximum textures minus one)</param>
165          public TexturePool(GpuContext context, GpuChannel channel, ulong address, int maximumId) : base(context, channel.MemoryManager.Physical, address, maximumId)
166          {
167              _channel = channel;
168              _aliasLists = new Dictionary<Texture, TextureAliasList>();
169          }
170  
171          /// <summary>
172          /// Gets the texture descripor and texture with the given ID with no bounds check or synchronization.
173          /// </summary>
174          /// <param name="id">ID of the texture. This is effectively a zero-based index</param>
175          /// <param name="texture">The texture with the given ID</param>
176          /// <returns>The texture descriptor with the given ID</returns>
177          private ref readonly TextureDescriptor GetInternal(int id, out Texture texture)
178          {
179              texture = Items[id];
180  
181              ref readonly TextureDescriptor descriptor = ref GetDescriptorRef(id);
182  
183              if (texture == null)
184              {
185                  texture = PhysicalMemory.TextureCache.FindShortCache(descriptor);
186  
187                  if (texture == null)
188                  {
189                      // The dereference queue can put our texture back on the cache.
190                      if ((texture = ProcessDereferenceQueue(id)) != null)
191                      {
192                          return ref descriptor;
193                      }
194  
195                      TextureInfo info = GetInfo(descriptor, out int layerSize);
196                      texture = PhysicalMemory.TextureCache.FindOrCreateTexture(_channel.MemoryManager, TextureSearchFlags.ForSampler, info, layerSize);
197  
198                      // If this happens, then the texture address is invalid, we can't add it to the cache.
199                      if (texture == null)
200                      {
201                          return ref descriptor;
202                      }
203                  }
204                  else
205                  {
206                      texture.SynchronizeMemory();
207                  }
208  
209                  Items[id] = texture;
210  
211                  texture.IncrementReferenceCount(this, id, descriptor.UnpackAddress());
212  
213                  DescriptorCache[id] = descriptor;
214              }
215              else
216              {
217                  // On the path above (texture not yet in the pool), memory is automatically synchronized on texture creation.
218                  texture.SynchronizeMemory();
219              }
220  
221              return ref descriptor;
222          }
223  
224          /// <summary>
225          /// Gets the texture with the given ID.
226          /// </summary>
227          /// <param name="id">ID of the texture. This is effectively a zero-based index</param>
228          /// <returns>The texture with the given ID</returns>
229          public override Texture Get(int id)
230          {
231              return Get(id, srgbSampler: true);
232          }
233  
234          /// <summary>
235          /// Gets the texture with the given ID.
236          /// </summary>
237          /// <param name="id">ID of the texture. This is effectively a zero-based index</param>
238          /// <param name="srgbSampler">Whether the texture is being accessed with a sampler that has sRGB conversion enabled</param>
239          /// <returns>The texture with the given ID</returns>
240          public Texture Get(int id, bool srgbSampler)
241          {
242              if ((uint)id >= Items.Length)
243              {
244                  return null;
245              }
246  
247              if (SequenceNumber != Context.SequenceNumber)
248              {
249                  SequenceNumber = Context.SequenceNumber;
250  
251                  SynchronizeMemory();
252              }
253  
254              GetForBinding(id, srgbSampler, out Texture texture);
255  
256              return texture;
257          }
258  
259          /// <summary>
260          /// Gets the texture descriptor and texture with the given ID.
261          /// </summary>
262          /// <remarks>
263          /// This method assumes that the pool has been manually synchronized before doing binding.
264          /// </remarks>
265          /// <param name="id">ID of the texture. This is effectively a zero-based index</param>
266          /// <param name="srgbSampler">Whether the texture is being accessed with a sampler that has sRGB conversion enabled</param>
267          /// <param name="texture">The texture with the given ID</param>
268          /// <returns>The texture descriptor with the given ID</returns>
269          public ref readonly TextureDescriptor GetForBinding(int id, bool srgbSampler, out Texture texture)
270          {
271              if ((uint)id >= Items.Length)
272              {
273                  texture = null;
274                  return ref _defaultDescriptor;
275              }
276  
277              // When getting for binding, assume the pool has already been synchronized.
278  
279              if (!srgbSampler)
280              {
281                  // If the sampler does not have the sRGB bit enabled, then the texture can't use a sRGB format.
282                  ref readonly TextureDescriptor tempDescriptor = ref GetDescriptorRef(id);
283  
284                  if (tempDescriptor.UnpackSrgb() && FormatTable.TryGetTextureFormat(tempDescriptor.UnpackFormat(), isSrgb: false, out FormatInfo formatInfo))
285                  {
286                      // Get a view of the texture with the right format.
287                      return ref GetForBinding(id, formatInfo, out texture);
288                  }
289              }
290  
291              return ref GetInternal(id, out texture);
292          }
293  
294          /// <summary>
295          /// Gets the texture descriptor and texture with the given ID.
296          /// </summary>
297          /// <remarks>
298          /// This method assumes that the pool has been manually synchronized before doing binding.
299          /// </remarks>
300          /// <param name="id">ID of the texture. This is effectively a zero-based index</param>
301          /// <param name="formatInfo">Texture format information</param>
302          /// <param name="texture">The texture with the given ID</param>
303          /// <returns>The texture descriptor with the given ID</returns>
304          public ref readonly TextureDescriptor GetForBinding(int id, FormatInfo formatInfo, out Texture texture)
305          {
306              if ((uint)id >= Items.Length)
307              {
308                  texture = null;
309                  return ref _defaultDescriptor;
310              }
311  
312              ref readonly TextureDescriptor descriptor = ref GetInternal(id, out texture);
313  
314              if (texture != null && formatInfo.Format != 0 && texture.Format != formatInfo.Format)
315              {
316                  if (!_aliasLists.TryGetValue(texture, out TextureAliasList aliasList))
317                  {
318                      _aliasLists.Add(texture, aliasList = new TextureAliasList());
319                  }
320  
321                  texture = aliasList.Find(formatInfo.Format);
322  
323                  if (texture == null)
324                  {
325                      TextureInfo info = GetInfo(descriptor, out int layerSize);
326                      info = ChangeFormat(info, formatInfo);
327                      texture = PhysicalMemory.TextureCache.FindOrCreateTexture(_channel.MemoryManager, TextureSearchFlags.ForSampler, info, layerSize);
328  
329                      if (texture != null)
330                      {
331                          aliasList.Add(formatInfo.Format, texture);
332                      }
333                  }
334              }
335  
336              return ref descriptor;
337          }
338  
339          /// <summary>
340          /// Checks if the pool was modified, and returns the last sequence number where a modification was detected.
341          /// </summary>
342          /// <returns>A number that increments each time a modification is detected</returns>
343          public int CheckModified()
344          {
345              if (SequenceNumber != Context.SequenceNumber)
346              {
347                  SequenceNumber = Context.SequenceNumber;
348  
349                  SynchronizeMemory();
350              }
351  
352              return ModifiedSequenceNumber;
353          }
354  
355          /// <summary>
356          /// Forcibly remove a texture from this pool's items.
357          /// If deferred, the dereference will be queued to occur on the render thread.
358          /// </summary>
359          /// <param name="texture">The texture being removed</param>
360          /// <param name="id">The ID of the texture in this pool</param>
361          /// <param name="deferred">If true, queue the dereference to happen on the render thread, otherwise dereference immediately</param>
362          public void ForceRemove(Texture texture, int id, bool deferred)
363          {
364              var previous = Interlocked.Exchange(ref Items[id], null);
365  
366              if (deferred)
367              {
368                  if (previous != null)
369                  {
370                      _dereferenceQueue.Enqueue(DereferenceRequest.Remove(texture));
371                  }
372              }
373              else
374              {
375                  texture.DecrementReferenceCount();
376                  RemoveAliasList(texture);
377              }
378          }
379  
380          /// <summary>
381          /// Queues a request to update a texture's mapping.
382          /// Mapping is updated later to avoid deleting the texture if it is still sparsely mapped.
383          /// </summary>
384          /// <param name="texture">Texture with potential mapping change</param>
385          /// <param name="id">ID in cache of texture with potential mapping change</param>
386          public void QueueUpdateMapping(Texture texture, int id)
387          {
388              if (Interlocked.Exchange(ref Items[id], null) == texture)
389              {
390                  _dereferenceQueue.Enqueue(DereferenceRequest.Remap(texture, id));
391              }
392          }
393  
394          /// <summary>
395          /// Process the dereference queue, decrementing the reference count for each texture in it.
396          /// This is used to ensure that texture disposal happens on the render thread.
397          /// </summary>
398          /// <param name="id">The ID of the entry that triggered this method</param>
399          /// <returns>Texture that matches the entry ID if it has been readded to the cache.</returns>
400          private Texture ProcessDereferenceQueue(int id = -1)
401          {
402              while (_dereferenceQueue.TryDequeue(out DereferenceRequest request))
403              {
404                  Texture texture = request.Texture;
405  
406                  // Unmapped storage textures can swap their ranges. The texture must be storage with no views or dependencies.
407                  // TODO: Would need to update ranges on views, or guarantee that ones where the range changes can be instantly deleted.
408  
409                  if (request.IsRemapped && texture.Group.Storage == texture && !texture.HasViews && !texture.Group.HasCopyDependencies)
410                  {
411                      // Has the mapping for this texture changed?
412                      ref readonly TextureDescriptor descriptor = ref GetDescriptorRef(request.ID);
413  
414                      ulong address = descriptor.UnpackAddress();
415  
416                      if (!descriptor.Equals(ref DescriptorCache[request.ID]))
417                      {
418                          // If the pool entry has already been replaced, just remove the texture.
419  
420                          texture.DecrementReferenceCount();
421                          continue;
422                      }
423  
424                      MultiRange range = _channel.MemoryManager.Physical.TextureCache.UpdatePartiallyMapped(_channel.MemoryManager, address, texture);
425  
426                      // If the texture is not mapped at all, delete its reference.
427  
428                      if (range.Count == 1 && range.GetSubRange(0).Address == MemoryManager.PteUnmapped)
429                      {
430                          texture.DecrementReferenceCount();
431                          continue;
432                      }
433  
434                      Items[request.ID] = texture;
435  
436                      // Create a new pool reference, as the last one was removed on unmap.
437  
438                      texture.IncrementReferenceCount(this, request.ID, address);
439                      texture.DecrementReferenceCount();
440  
441                      // Refetch the range. Changes since the last check could have been lost
442                      // as the cache entry was not restored (required to queue mapping change).
443  
444                      range = _channel.MemoryManager.GetPhysicalRegions(address, texture.Size);
445  
446                      if (!range.Equals(texture.Range))
447                      {
448                          // Part of the texture was mapped or unmapped. Replace the range and regenerate tracking handles.
449                          if (!_channel.MemoryManager.Physical.TextureCache.UpdateMapping(texture, range))
450                          {
451                              // Texture could not be remapped due to a collision, just delete it.
452                              if (Interlocked.Exchange(ref Items[request.ID], null) != null)
453                              {
454                                  // If this is null, a request was already queued to decrement reference.
455                                  texture.DecrementReferenceCount(this, request.ID);
456                              }
457                              continue;
458                          }
459                      }
460  
461                      if (request.ID == id)
462                      {
463                          return texture;
464                      }
465                  }
466                  else
467                  {
468                      texture.DecrementReferenceCount();
469                  }
470  
471                  RemoveAliasList(texture);
472              }
473  
474              return null;
475          }
476  
477          /// <summary>
478          /// Implementation of the texture pool range invalidation.
479          /// </summary>
480          /// <param name="address">Start address of the range of the texture pool</param>
481          /// <param name="size">Size of the range being invalidated</param>
482          protected override void InvalidateRangeImpl(ulong address, ulong size)
483          {
484              ProcessDereferenceQueue();
485  
486              ulong endAddress = address + size;
487  
488              for (; address < endAddress; address += DescriptorSize)
489              {
490                  int id = (int)((address - Address) / DescriptorSize);
491  
492                  Texture texture = Items[id];
493  
494                  if (texture != null)
495                  {
496                      ref TextureDescriptor cachedDescriptor = ref DescriptorCache[id];
497                      ref readonly TextureDescriptor descriptor = ref GetDescriptorRefAddress(address);
498  
499                      // If the descriptors are the same, the texture is the same,
500                      // we don't need to remove as it was not modified. Just continue.
501                      if (descriptor.Equals(ref cachedDescriptor))
502                      {
503                          continue;
504                      }
505  
506                      if (texture.HasOneReference())
507                      {
508                          _channel.MemoryManager.Physical.TextureCache.AddShortCache(texture, ref cachedDescriptor);
509                      }
510  
511                      if (Interlocked.Exchange(ref Items[id], null) != null)
512                      {
513                          texture.DecrementReferenceCount(this, id);
514                          RemoveAliasList(texture);
515                      }
516                  }
517              }
518          }
519  
520          /// <summary>
521          /// Gets texture information from a texture descriptor.
522          /// </summary>
523          /// <param name="descriptor">The texture descriptor</param>
524          /// <param name="layerSize">Layer size for textures using a sub-range of mipmap levels, otherwise 0</param>
525          /// <returns>The texture information</returns>
526          private static TextureInfo GetInfo(in TextureDescriptor descriptor, out int layerSize)
527          {
528              int depthOrLayers = descriptor.UnpackDepth();
529              int levels = descriptor.UnpackLevels();
530  
531              TextureMsaaMode msaaMode = descriptor.UnpackTextureMsaaMode();
532  
533              int samplesInX = msaaMode.SamplesInX();
534              int samplesInY = msaaMode.SamplesInY();
535  
536              int stride = descriptor.UnpackStride();
537  
538              TextureDescriptorType descriptorType = descriptor.UnpackTextureDescriptorType();
539  
540              bool isLinear = descriptorType == TextureDescriptorType.Linear;
541  
542              Target target = descriptor.UnpackTextureTarget().Convert((samplesInX | samplesInY) != 1);
543  
544              int width = target == Target.TextureBuffer ? descriptor.UnpackBufferTextureWidth() : descriptor.UnpackWidth();
545              int height = descriptor.UnpackHeight();
546  
547              if (target == Target.Texture2DMultisample || target == Target.Texture2DMultisampleArray)
548              {
549                  // This is divided back before the backend texture is created.
550                  width *= samplesInX;
551                  height *= samplesInY;
552              }
553  
554              // We use 2D targets for 1D textures as that makes texture cache
555              // management easier. We don't know the target for render target
556              // and copies, so those would normally use 2D targets, which are
557              // not compatible with 1D targets. By doing that we also allow those
558              // to match when looking for compatible textures on the cache.
559              if (target == Target.Texture1D)
560              {
561                  target = Target.Texture2D;
562                  height = 1;
563              }
564              else if (target == Target.Texture1DArray)
565              {
566                  target = Target.Texture2DArray;
567                  height = 1;
568              }
569  
570              uint format = descriptor.UnpackFormat();
571              bool srgb = descriptor.UnpackSrgb();
572  
573              ulong gpuVa = descriptor.UnpackAddress();
574  
575              if (!FormatTable.TryGetTextureFormat(format, srgb, out FormatInfo formatInfo))
576              {
577                  if (gpuVa != 0 && format != 0)
578                  {
579                      Logger.Error?.Print(LogClass.Gpu, $"Invalid texture format 0x{format:X} (sRGB: {srgb}).");
580                  }
581  
582                  formatInfo = FormatInfo.Default;
583              }
584  
585              int gobBlocksInY = descriptor.UnpackGobBlocksInY();
586              int gobBlocksInZ = descriptor.UnpackGobBlocksInZ();
587  
588              int gobBlocksInTileX = descriptor.UnpackGobBlocksInTileX();
589  
590              layerSize = 0;
591  
592              int minLod = descriptor.UnpackBaseLevel();
593              int maxLod = descriptor.UnpackMaxLevelInclusive();
594  
595              // Linear textures don't support mipmaps, so we don't handle this case here.
596              if ((minLod != 0 || maxLod + 1 != levels) && target != Target.TextureBuffer && !isLinear)
597              {
598                  int depth = TextureInfo.GetDepth(target, depthOrLayers);
599                  int layers = TextureInfo.GetLayers(target, depthOrLayers);
600  
601                  SizeInfo sizeInfo = SizeCalculator.GetBlockLinearTextureSize(
602                      width,
603                      height,
604                      depth,
605                      levels,
606                      layers,
607                      formatInfo.BlockWidth,
608                      formatInfo.BlockHeight,
609                      formatInfo.BytesPerPixel,
610                      gobBlocksInY,
611                      gobBlocksInZ,
612                      gobBlocksInTileX);
613  
614                  layerSize = sizeInfo.LayerSize;
615  
616                  if (minLod != 0 && minLod < levels)
617                  {
618                      // If the base level is not zero, we additionally add the mip level offset
619                      // to the address, this allows the texture manager to find the base level from the
620                      // address if there is a overlapping texture on the cache that can contain the new texture.
621                      gpuVa += (ulong)sizeInfo.GetMipOffset(minLod);
622  
623                      width = Math.Max(1, width >> minLod);
624                      height = Math.Max(1, height >> minLod);
625  
626                      if (target == Target.Texture3D)
627                      {
628                          depthOrLayers = Math.Max(1, depthOrLayers >> minLod);
629                      }
630  
631                      (gobBlocksInY, gobBlocksInZ) = SizeCalculator.GetMipGobBlockSizes(height, depth, formatInfo.BlockHeight, gobBlocksInY, gobBlocksInZ, minLod);
632                  }
633  
634                  levels = (maxLod - minLod) + 1;
635              }
636  
637              levels = ClampLevels(target, width, height, depthOrLayers, levels);
638  
639              SwizzleComponent swizzleR = descriptor.UnpackSwizzleR().Convert();
640              SwizzleComponent swizzleG = descriptor.UnpackSwizzleG().Convert();
641              SwizzleComponent swizzleB = descriptor.UnpackSwizzleB().Convert();
642              SwizzleComponent swizzleA = descriptor.UnpackSwizzleA().Convert();
643  
644              DepthStencilMode depthStencilMode = GetDepthStencilMode(
645                  formatInfo.Format,
646                  swizzleR,
647                  swizzleG,
648                  swizzleB,
649                  swizzleA);
650  
651              if (formatInfo.Format.IsDepthOrStencil())
652              {
653                  swizzleR = SwizzleComponent.Red;
654                  swizzleG = SwizzleComponent.Red;
655                  swizzleB = SwizzleComponent.Red;
656  
657                  if (depthStencilMode == DepthStencilMode.Depth)
658                  {
659                      swizzleA = SwizzleComponent.One;
660                  }
661                  else
662                  {
663                      swizzleA = SwizzleComponent.Red;
664                  }
665              }
666  
667              return new TextureInfo(
668                  gpuVa,
669                  width,
670                  height,
671                  depthOrLayers,
672                  levels,
673                  samplesInX,
674                  samplesInY,
675                  stride,
676                  isLinear,
677                  gobBlocksInY,
678                  gobBlocksInZ,
679                  gobBlocksInTileX,
680                  target,
681                  formatInfo,
682                  depthStencilMode,
683                  swizzleR,
684                  swizzleG,
685                  swizzleB,
686                  swizzleA);
687          }
688  
689          /// <summary>
690          /// Clamps the amount of mipmap levels to the maximum allowed for the given texture dimensions.
691          /// </summary>
692          /// <param name="target">Number of texture dimensions (1D, 2D, 3D, Cube, etc)</param>
693          /// <param name="width">Width of the texture</param>
694          /// <param name="height">Height of the texture, ignored for 1D textures</param>
695          /// <param name="depthOrLayers">Depth of the texture for 3D textures, otherwise ignored</param>
696          /// <param name="levels">Original amount of mipmap levels</param>
697          /// <returns>Clamped mipmap levels</returns>
698          private static int ClampLevels(Target target, int width, int height, int depthOrLayers, int levels)
699          {
700              int maxSize = width;
701  
702              if (target != Target.Texture1D &&
703                  target != Target.Texture1DArray)
704              {
705                  maxSize = Math.Max(maxSize, height);
706              }
707  
708              if (target == Target.Texture3D)
709              {
710                  maxSize = Math.Max(maxSize, depthOrLayers);
711              }
712  
713              int maxLevels = BitOperations.Log2((uint)maxSize) + 1;
714              return Math.Min(levels, maxLevels);
715          }
716  
717          /// <summary>
718          /// Gets the texture depth-stencil mode, based on the swizzle components of each color channel.
719          /// The depth-stencil mode is determined based on how the driver sets those parameters.
720          /// </summary>
721          /// <param name="format">The format of the texture</param>
722          /// <param name="components">The texture swizzle components</param>
723          /// <returns>The depth-stencil mode</returns>
724          private static DepthStencilMode GetDepthStencilMode(Format format, params SwizzleComponent[] components)
725          {
726              // R = Depth, G = Stencil.
727              // On 24-bits depth formats, this is inverted (Stencil is R etc).
728              // NVN setup:
729              // For depth, A is set to 1.0f, the other components are set to Depth.
730              // For stencil, all components are set to Stencil.
731              SwizzleComponent component = components[0];
732  
733              for (int index = 1; index < 4 && !IsRG(component); index++)
734              {
735                  component = components[index];
736              }
737  
738              if (!IsRG(component))
739              {
740                  return DepthStencilMode.Depth;
741              }
742  
743              if (format == Format.D24UnormS8Uint)
744              {
745                  return component == SwizzleComponent.Red
746                      ? DepthStencilMode.Stencil
747                      : DepthStencilMode.Depth;
748              }
749              else
750              {
751                  return component == SwizzleComponent.Red
752                      ? DepthStencilMode.Depth
753                      : DepthStencilMode.Stencil;
754              }
755          }
756  
757          /// <summary>
758          /// Checks if the swizzle component is equal to the red or green channels.
759          /// </summary>
760          /// <param name="component">The swizzle component to check</param>
761          /// <returns>True if the swizzle component is equal to the red or green, false otherwise</returns>
762          private static bool IsRG(SwizzleComponent component)
763          {
764              return component == SwizzleComponent.Red ||
765                     component == SwizzleComponent.Green;
766          }
767  
768          /// <summary>
769          /// Changes the format on the texture information structure, and also adjusts the width for the new format if needed.
770          /// </summary>
771          /// <param name="info">Texture information</param>
772          /// <param name="dstFormat">New format</param>
773          /// <returns>Texture information with the new format</returns>
774          private static TextureInfo ChangeFormat(in TextureInfo info, FormatInfo dstFormat)
775          {
776              int width = info.Width;
777  
778              if (info.FormatInfo.BytesPerPixel != dstFormat.BytesPerPixel)
779              {
780                  int stride = width * info.FormatInfo.BytesPerPixel;
781                  width = stride / dstFormat.BytesPerPixel;
782              }
783  
784              return new TextureInfo(
785                  info.GpuAddress,
786                  width,
787                  info.Height,
788                  info.DepthOrLayers,
789                  info.Levels,
790                  info.SamplesInX,
791                  info.SamplesInY,
792                  info.Stride,
793                  info.IsLinear,
794                  info.GobBlocksInY,
795                  info.GobBlocksInZ,
796                  info.GobBlocksInTileX,
797                  info.Target,
798                  dstFormat,
799                  info.DepthStencilMode,
800                  info.SwizzleR,
801                  info.SwizzleG,
802                  info.SwizzleB,
803                  info.SwizzleA);
804          }
805  
806          /// <summary>
807          /// Removes all aliases for a texture.
808          /// </summary>
809          /// <param name="texture">Texture to have the aliases removed</param>
810          private void RemoveAliasList(Texture texture)
811          {
812              if (_aliasLists.TryGetValue(texture, out TextureAliasList aliasList))
813              {
814                  _aliasLists.Remove(texture);
815                  aliasList.Destroy();
816              }
817          }
818  
819          /// <summary>
820          /// Decrements the reference count of the texture.
821          /// This indicates that the texture pool is not using it anymore.
822          /// </summary>
823          /// <param name="item">The texture to be deleted</param>
824          protected override void Delete(Texture item)
825          {
826              if (item != null)
827              {
828                  item.DecrementReferenceCount(this);
829                  RemoveAliasList(item);
830              }
831          }
832  
833          public override void Dispose()
834          {
835              ProcessDereferenceQueue();
836  
837              base.Dispose();
838          }
839      }
840  }