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