Pool.cs 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. using Ryujinx.Cpu.Tracking;
  2. using Ryujinx.Graphics.Gpu.Memory;
  3. using System;
  4. using System.Runtime.InteropServices;
  5. namespace Ryujinx.Graphics.Gpu.Image
  6. {
  7. /// <summary>
  8. /// Represents a pool of GPU resources, such as samplers or textures.
  9. /// </summary>
  10. /// <typeparam name="T1">Type of the GPU resource</typeparam>
  11. /// <typeparam name="T2">Type of the descriptor</typeparam>
  12. abstract class Pool<T1, T2> : IDisposable where T2 : unmanaged
  13. {
  14. protected const int DescriptorSize = 0x20;
  15. protected GpuContext Context;
  16. protected PhysicalMemory PhysicalMemory;
  17. protected int SequenceNumber;
  18. protected int ModifiedSequenceNumber;
  19. protected T1[] Items;
  20. protected T2[] DescriptorCache;
  21. /// <summary>
  22. /// The maximum ID value of resources on the pool (inclusive).
  23. /// </summary>
  24. /// <remarks>
  25. /// The maximum amount of resources on the pool is equal to this value plus one.
  26. /// </remarks>
  27. public int MaximumId { get; }
  28. /// <summary>
  29. /// The address of the pool in guest memory.
  30. /// </summary>
  31. public ulong Address { get; }
  32. /// <summary>
  33. /// The size of the pool in bytes.
  34. /// </summary>
  35. public ulong Size { get; }
  36. private readonly CpuMultiRegionHandle _memoryTracking;
  37. private readonly Action<ulong, ulong> _modifiedDelegate;
  38. private int _modifiedSequenceOffset;
  39. private bool _modified;
  40. /// <summary>
  41. /// Creates a new instance of the GPU resource pool.
  42. /// </summary>
  43. /// <param name="context">GPU context that the pool belongs to</param>
  44. /// <param name="physicalMemory">Physical memory where the resource descriptors are mapped</param>
  45. /// <param name="address">Address of the pool in physical memory</param>
  46. /// <param name="maximumId">Maximum index of an item on the pool (inclusive)</param>
  47. public Pool(GpuContext context, PhysicalMemory physicalMemory, ulong address, int maximumId)
  48. {
  49. Context = context;
  50. PhysicalMemory = physicalMemory;
  51. MaximumId = maximumId;
  52. int count = maximumId + 1;
  53. ulong size = (ulong)(uint)count * DescriptorSize;
  54. Items = new T1[count];
  55. DescriptorCache = new T2[count];
  56. Address = address;
  57. Size = size;
  58. _memoryTracking = physicalMemory.BeginGranularTracking(address, size);
  59. _memoryTracking.RegisterPreciseAction(address, size, PreciseAction);
  60. _modifiedDelegate = RegionModified;
  61. }
  62. /// <summary>
  63. /// Gets the descriptor for a given ID.
  64. /// </summary>
  65. /// <param name="id">ID of the descriptor. This is effectively a zero-based index</param>
  66. /// <returns>The descriptor</returns>
  67. public T2 GetDescriptor(int id)
  68. {
  69. return PhysicalMemory.Read<T2>(Address + (ulong)id * DescriptorSize);
  70. }
  71. /// <summary>
  72. /// Gets a reference to the descriptor for a given ID.
  73. /// </summary>
  74. /// <param name="id">ID of the descriptor. This is effectively a zero-based index</param>
  75. /// <returns>A reference to the descriptor</returns>
  76. public ref readonly T2 GetDescriptorRef(int id)
  77. {
  78. return ref MemoryMarshal.Cast<byte, T2>(PhysicalMemory.GetSpan(Address + (ulong)id * DescriptorSize, DescriptorSize))[0];
  79. }
  80. /// <summary>
  81. /// Gets the GPU resource with the given ID.
  82. /// </summary>
  83. /// <param name="id">ID of the resource. This is effectively a zero-based index</param>
  84. /// <returns>The GPU resource with the given ID</returns>
  85. public abstract T1 Get(int id);
  86. /// <summary>
  87. /// Checks if a given ID is valid and inside the range of the pool.
  88. /// </summary>
  89. /// <param name="id">ID of the descriptor. This is effectively a zero-based index</param>
  90. /// <returns>True if the specified ID is valid, false otherwise</returns>
  91. public bool IsValidId(int id)
  92. {
  93. return (uint)id <= MaximumId;
  94. }
  95. /// <summary>
  96. /// Synchronizes host memory with guest memory.
  97. /// This causes invalidation of pool entries,
  98. /// if a modification of entries by the CPU is detected.
  99. /// </summary>
  100. public void SynchronizeMemory()
  101. {
  102. _modified = false;
  103. _memoryTracking.QueryModified(_modifiedDelegate);
  104. if (_modified)
  105. {
  106. UpdateModifiedSequence();
  107. }
  108. }
  109. /// <summary>
  110. /// Indicate that a region of the pool was modified, and must be loaded from memory.
  111. /// </summary>
  112. /// <param name="mAddress">Start address of the modified region</param>
  113. /// <param name="mSize">Size of the modified region</param>
  114. private void RegionModified(ulong mAddress, ulong mSize)
  115. {
  116. _modified = true;
  117. if (mAddress < Address)
  118. {
  119. mAddress = Address;
  120. }
  121. ulong maxSize = Address + Size - mAddress;
  122. if (mSize > maxSize)
  123. {
  124. mSize = maxSize;
  125. }
  126. InvalidateRangeImpl(mAddress, mSize);
  127. }
  128. /// <summary>
  129. /// Updates the modified sequence number using the current sequence number and offset,
  130. /// indicating that it has been modified.
  131. /// </summary>
  132. protected void UpdateModifiedSequence()
  133. {
  134. ModifiedSequenceNumber = SequenceNumber + _modifiedSequenceOffset;
  135. }
  136. /// <summary>
  137. /// An action to be performed when a precise memory access occurs to this resource.
  138. /// Makes sure that the dirty flags are checked.
  139. /// </summary>
  140. /// <param name="address">Address of the memory action</param>
  141. /// <param name="size">Size in bytes</param>
  142. /// <param name="write">True if the access was a write, false otherwise</param>
  143. private bool PreciseAction(ulong address, ulong size, bool write)
  144. {
  145. if (write && Context.SequenceNumber == SequenceNumber)
  146. {
  147. if (ModifiedSequenceNumber == SequenceNumber + _modifiedSequenceOffset)
  148. {
  149. // The modified sequence number is offset when PreciseActions occur so that
  150. // users checking it will see an increment and know the pool has changed since
  151. // their last look, even though the main SequenceNumber has not been changed.
  152. _modifiedSequenceOffset++;
  153. }
  154. // Force the pool to be checked again the next time it is used.
  155. SequenceNumber--;
  156. }
  157. return false;
  158. }
  159. protected abstract void InvalidateRangeImpl(ulong address, ulong size);
  160. protected abstract void Delete(T1 item);
  161. /// <summary>
  162. /// Performs the disposal of all resources stored on the pool.
  163. /// It's an error to try using the pool after disposal.
  164. /// </summary>
  165. public virtual void Dispose()
  166. {
  167. if (Items != null)
  168. {
  169. for (int index = 0; index < Items.Length; index++)
  170. {
  171. Delete(Items[index]);
  172. }
  173. Items = null;
  174. }
  175. _memoryTracking.Dispose();
  176. }
  177. }
  178. }