TexturePool.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. using Ryujinx.Common.Logging;
  2. using Ryujinx.Graphics.GAL;
  3. using Ryujinx.Graphics.Gpu.Memory;
  4. using System.Collections.Generic;
  5. namespace Ryujinx.Graphics.Gpu.Image
  6. {
  7. /// <summary>
  8. /// Texture pool.
  9. /// </summary>
  10. class TexturePool : Pool<Texture>
  11. {
  12. private int _sequenceNumber;
  13. /// <summary>
  14. /// Intrusive linked list node used on the texture pool cache.
  15. /// </summary>
  16. public LinkedListNode<TexturePool> CacheNode { get; set; }
  17. /// <summary>
  18. /// Constructs a new instance of the texture pool.
  19. /// </summary>
  20. /// <param name="context">GPU context that the texture pool belongs to</param>
  21. /// <param name="address">Address of the texture pool in guest memory</param>
  22. /// <param name="maximumId">Maximum texture ID of the texture pool (equal to maximum textures minus one)</param>
  23. public TexturePool(GpuContext context, ulong address, int maximumId) : base(context, address, maximumId) { }
  24. /// <summary>
  25. /// Gets the texture with the given ID.
  26. /// </summary>
  27. /// <param name="id">ID of the texture. This is effectively a zero-based index</param>
  28. /// <returns>The texture with the given ID</returns>
  29. public override Texture Get(int id)
  30. {
  31. if ((uint)id >= Items.Length)
  32. {
  33. return null;
  34. }
  35. if (_sequenceNumber != Context.SequenceNumber)
  36. {
  37. _sequenceNumber = Context.SequenceNumber;
  38. SynchronizeMemory();
  39. }
  40. Texture texture = Items[id];
  41. if (texture == null)
  42. {
  43. TextureDescriptor descriptor = GetDescriptor(id);
  44. TextureInfo info = GetInfo(descriptor);
  45. // Bad address. We can't add a texture with a invalid address
  46. // to the cache.
  47. if (info.Address == MemoryManager.BadAddress)
  48. {
  49. return null;
  50. }
  51. texture = Context.Methods.TextureManager.FindOrCreateTexture(info, TextureSearchFlags.ForSampler);
  52. texture.IncrementReferenceCount();
  53. Items[id] = texture;
  54. }
  55. else
  56. {
  57. if (texture.ChangedSize)
  58. {
  59. // Texture changed size at one point - it may be a different size than the sampler expects.
  60. // This can be triggered when the size is changed by a size hint on copy or draw, but the texture has been sampled before.
  61. TextureDescriptor descriptor = GetDescriptor(id);
  62. int width = descriptor.UnpackWidth();
  63. int height = descriptor.UnpackHeight();
  64. if (texture.Info.Width != width || texture.Info.Height != height)
  65. {
  66. texture.ChangeSize(width, height, texture.Info.DepthOrLayers);
  67. }
  68. }
  69. // Memory is automatically synchronized on texture creation.
  70. texture.SynchronizeMemory();
  71. }
  72. return texture;
  73. }
  74. /// <summary>
  75. /// Gets the texture descriptor from a given texture ID.
  76. /// </summary>
  77. /// <param name="id">ID of the texture. This is effectively a zero-based index</param>
  78. /// <returns>The texture descriptor</returns>
  79. public TextureDescriptor GetDescriptor(int id)
  80. {
  81. return Context.PhysicalMemory.Read<TextureDescriptor>(Address + (ulong)id * DescriptorSize);
  82. }
  83. /// <summary>
  84. /// Implementation of the texture pool range invalidation.
  85. /// </summary>
  86. /// <param name="address">Start address of the range of the texture pool</param>
  87. /// <param name="size">Size of the range being invalidated</param>
  88. protected override void InvalidateRangeImpl(ulong address, ulong size)
  89. {
  90. ulong endAddress = address + size;
  91. for (; address < endAddress; address += DescriptorSize)
  92. {
  93. int id = (int)((address - Address) / DescriptorSize);
  94. Texture texture = Items[id];
  95. if (texture != null)
  96. {
  97. TextureDescriptor descriptor = Context.PhysicalMemory.Read<TextureDescriptor>(address);
  98. // If the descriptors are the same, the texture is the same,
  99. // we don't need to remove as it was not modified. Just continue.
  100. if (texture.IsPerfectMatch(GetInfo(descriptor), TextureSearchFlags.Strict))
  101. {
  102. continue;
  103. }
  104. texture.DecrementReferenceCount();
  105. Items[id] = null;
  106. }
  107. }
  108. }
  109. /// <summary>
  110. /// Gets texture information from a texture descriptor.
  111. /// </summary>
  112. /// <param name="descriptor">The texture descriptor</param>
  113. /// <returns>The texture information</returns>
  114. private TextureInfo GetInfo(TextureDescriptor descriptor)
  115. {
  116. ulong address = Context.MemoryManager.Translate(descriptor.UnpackAddress());
  117. int width = descriptor.UnpackWidth();
  118. int height = descriptor.UnpackHeight();
  119. int depthOrLayers = descriptor.UnpackDepth();
  120. int levels = descriptor.UnpackLevels();
  121. TextureMsaaMode msaaMode = descriptor.UnpackTextureMsaaMode();
  122. int samplesInX = msaaMode.SamplesInX();
  123. int samplesInY = msaaMode.SamplesInY();
  124. int stride = descriptor.UnpackStride();
  125. TextureDescriptorType descriptorType = descriptor.UnpackTextureDescriptorType();
  126. bool isLinear = descriptorType == TextureDescriptorType.Linear;
  127. Target target = descriptor.UnpackTextureTarget().Convert((samplesInX | samplesInY) != 1);
  128. // We use 2D targets for 1D textures as that makes texture cache
  129. // management easier. We don't know the target for render target
  130. // and copies, so those would normally use 2D targets, which are
  131. // not compatible with 1D targets. By doing that we also allow those
  132. // to match when looking for compatible textures on the cache.
  133. if (target == Target.Texture1D)
  134. {
  135. target = Target.Texture2D;
  136. height = 1;
  137. }
  138. else if (target == Target.Texture1DArray)
  139. {
  140. target = Target.Texture2DArray;
  141. height = 1;
  142. }
  143. uint format = descriptor.UnpackFormat();
  144. bool srgb = descriptor.UnpackSrgb();
  145. if (!FormatTable.TryGetTextureFormat(format, srgb, out FormatInfo formatInfo))
  146. {
  147. if ((long)address > 0L && (int)format > 0)
  148. {
  149. Logger.Error?.Print(LogClass.Gpu, $"Invalid texture format 0x{format:X} (sRGB: {srgb}).");
  150. }
  151. formatInfo = FormatInfo.Default;
  152. }
  153. int gobBlocksInY = descriptor.UnpackGobBlocksInY();
  154. int gobBlocksInZ = descriptor.UnpackGobBlocksInZ();
  155. int gobBlocksInTileX = descriptor.UnpackGobBlocksInTileX();
  156. SwizzleComponent swizzleR = descriptor.UnpackSwizzleR().Convert();
  157. SwizzleComponent swizzleG = descriptor.UnpackSwizzleG().Convert();
  158. SwizzleComponent swizzleB = descriptor.UnpackSwizzleB().Convert();
  159. SwizzleComponent swizzleA = descriptor.UnpackSwizzleA().Convert();
  160. DepthStencilMode depthStencilMode = GetDepthStencilMode(
  161. formatInfo.Format,
  162. swizzleR,
  163. swizzleG,
  164. swizzleB,
  165. swizzleA);
  166. if (formatInfo.Format.IsDepthOrStencil())
  167. {
  168. swizzleR = SwizzleComponent.Red;
  169. swizzleG = SwizzleComponent.Red;
  170. swizzleB = SwizzleComponent.Red;
  171. if (depthStencilMode == DepthStencilMode.Depth)
  172. {
  173. swizzleA = SwizzleComponent.One;
  174. }
  175. else
  176. {
  177. swizzleA = SwizzleComponent.Red;
  178. }
  179. }
  180. return new TextureInfo(
  181. address,
  182. width,
  183. height,
  184. depthOrLayers,
  185. levels,
  186. samplesInX,
  187. samplesInY,
  188. stride,
  189. isLinear,
  190. gobBlocksInY,
  191. gobBlocksInZ,
  192. gobBlocksInTileX,
  193. target,
  194. formatInfo,
  195. depthStencilMode,
  196. swizzleR,
  197. swizzleG,
  198. swizzleB,
  199. swizzleA);
  200. }
  201. /// <summary>
  202. /// Gets the texture depth-stencil mode, based on the swizzle components of each color channel.
  203. /// The depth-stencil mode is determined based on how the driver sets those parameters.
  204. /// </summary>
  205. /// <param name="format">The format of the texture</param>
  206. /// <param name="components">The texture swizzle components</param>
  207. /// <returns>The depth-stencil mode</returns>
  208. private static DepthStencilMode GetDepthStencilMode(Format format, params SwizzleComponent[] components)
  209. {
  210. // R = Depth, G = Stencil.
  211. // On 24-bits depth formats, this is inverted (Stencil is R etc).
  212. // NVN setup:
  213. // For depth, A is set to 1.0f, the other components are set to Depth.
  214. // For stencil, all components are set to Stencil.
  215. SwizzleComponent component = components[0];
  216. for (int index = 1; index < 4 && !IsRG(component); index++)
  217. {
  218. component = components[index];
  219. }
  220. if (!IsRG(component))
  221. {
  222. return DepthStencilMode.Depth;
  223. }
  224. if (format == Format.D24X8Unorm || format == Format.D24UnormS8Uint)
  225. {
  226. return component == SwizzleComponent.Red
  227. ? DepthStencilMode.Stencil
  228. : DepthStencilMode.Depth;
  229. }
  230. else
  231. {
  232. return component == SwizzleComponent.Red
  233. ? DepthStencilMode.Depth
  234. : DepthStencilMode.Stencil;
  235. }
  236. }
  237. /// <summary>
  238. /// Checks if the swizzle component is equal to the red or green channels.
  239. /// </summary>
  240. /// <param name="component">The swizzle component to check</param>
  241. /// <returns>True if the swizzle component is equal to the red or green, false otherwise</returns>
  242. private static bool IsRG(SwizzleComponent component)
  243. {
  244. return component == SwizzleComponent.Red ||
  245. component == SwizzleComponent.Green;
  246. }
  247. /// <summary>
  248. /// Decrements the reference count of the texture.
  249. /// This indicates that the texture pool is not using it anymore.
  250. /// </summary>
  251. /// <param name="item">The texture to be deleted</param>
  252. protected override void Delete(Texture item)
  253. {
  254. item?.DecrementReferenceCount();
  255. }
  256. }
  257. }