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 }