/ src / Ryujinx.Graphics.Gpu / Memory / CounterCache.cs
CounterCache.cs
  1  using Ryujinx.Graphics.GAL;
  2  using System.Collections.Generic;
  3  
  4  namespace Ryujinx.Graphics.Gpu.Memory
  5  {
  6      /// <summary>
  7      /// Represents the GPU counter cache.
  8      /// </summary>
  9      class CounterCache
 10      {
 11          private readonly struct CounterEntry
 12          {
 13              public ulong Address { get; }
 14              public ICounterEvent Event { get; }
 15  
 16              public CounterEntry(ulong address, ICounterEvent evt)
 17              {
 18                  Address = address;
 19                  Event = evt;
 20              }
 21          }
 22  
 23          private readonly List<CounterEntry> _items;
 24  
 25          /// <summary>
 26          /// Creates a new instance of the GPU counter cache.
 27          /// </summary>
 28          public CounterCache()
 29          {
 30              _items = new List<CounterEntry>();
 31          }
 32  
 33          /// <summary>
 34          /// Adds a new counter to the counter cache, or updates a existing one.
 35          /// </summary>
 36          /// <param name="gpuVa">GPU virtual address where the counter will be written in memory</param>
 37          /// <param name="evt">The new counter</param>
 38          public void AddOrUpdate(ulong gpuVa, ICounterEvent evt)
 39          {
 40              int index = BinarySearch(gpuVa);
 41  
 42              CounterEntry entry = new(gpuVa, evt);
 43  
 44              if (index < 0)
 45              {
 46                  _items.Insert(~index, entry);
 47              }
 48              else
 49              {
 50                  _items[index] = entry;
 51              }
 52          }
 53  
 54          /// <summary>
 55          /// Handles removal of counters written to a memory region being unmapped.
 56          /// </summary>
 57          /// <param name="sender">Sender object</param>
 58          /// <param name="e">Event arguments</param>
 59          public void MemoryUnmappedHandler(object sender, UnmapEventArgs e) => RemoveRange(e.Address, e.Size);
 60  
 61          private void RemoveRange(ulong gpuVa, ulong size)
 62          {
 63              int index = BinarySearch(gpuVa + size - 1);
 64  
 65              if (index < 0)
 66              {
 67                  index = ~index;
 68              }
 69  
 70              if (index >= _items.Count || !InRange(gpuVa, size, _items[index].Address))
 71              {
 72                  return;
 73              }
 74  
 75              int count = 1;
 76  
 77              while (index > 0 && InRange(gpuVa, size, _items[index - 1].Address))
 78              {
 79                  index--;
 80                  count++;
 81              }
 82  
 83              // Notify the removed counter events that their result should no longer be written out.
 84              for (int i = 0; i < count; i++)
 85              {
 86                  ICounterEvent evt = _items[index + i].Event;
 87                  if (evt != null)
 88                  {
 89                      evt.Invalid = true;
 90                  }
 91              }
 92  
 93              _items.RemoveRange(index, count);
 94          }
 95  
 96          /// <summary>
 97          /// Checks whenever an address falls inside a given range.
 98          /// </summary>
 99          /// <param name="startVa">Range start address</param>
100          /// <param name="size">Range size</param>
101          /// <param name="gpuVa">Address being checked</param>
102          /// <returns>True if the address falls inside the range, false otherwise</returns>
103          private static bool InRange(ulong startVa, ulong size, ulong gpuVa)
104          {
105              return gpuVa >= startVa && gpuVa < startVa + size;
106          }
107  
108          /// <summary>
109          /// Check if any counter value was written to the specified GPU virtual memory address.
110          /// </summary>
111          /// <param name="gpuVa">GPU virtual address</param>
112          /// <returns>True if any counter value was written on the specified address, false otherwise</returns>
113          public bool Contains(ulong gpuVa)
114          {
115              return BinarySearch(gpuVa) >= 0;
116          }
117  
118          /// <summary>
119          /// Flush any counter value written to the specified GPU virtual memory address.
120          /// </summary>
121          /// <param name="gpuVa">GPU virtual address</param>
122          /// <returns>True if any counter value was written on the specified address, false otherwise</returns>
123          public bool FindAndFlush(ulong gpuVa)
124          {
125              int index = BinarySearch(gpuVa);
126              if (index > 0)
127              {
128                  _items[index].Event?.Flush();
129  
130                  return true;
131              }
132              else
133              {
134                  return false;
135              }
136          }
137  
138          /// <summary>
139          /// Find any counter event that would write to the specified GPU virtual memory address.
140          /// </summary>
141          /// <param name="gpuVa">GPU virtual address</param>
142          /// <returns>The counter event, or null if not present</returns>
143          public ICounterEvent FindEvent(ulong gpuVa)
144          {
145              int index = BinarySearch(gpuVa);
146              if (index > 0)
147              {
148                  return _items[index].Event;
149              }
150              else
151              {
152                  return null;
153              }
154          }
155  
156          /// <summary>
157          /// Performs binary search of an address on the list.
158          /// </summary>
159          /// <param name="address">Address to search</param>
160          /// <returns>Index of the item, or complement of the index of the nearest item with lower value</returns>
161          private int BinarySearch(ulong address)
162          {
163              int left = 0;
164              int right = _items.Count - 1;
165  
166              while (left <= right)
167              {
168                  int range = right - left;
169  
170                  int middle = left + (range >> 1);
171  
172                  CounterEntry item = _items[middle];
173  
174                  if (item.Address == address)
175                  {
176                      return middle;
177                  }
178  
179                  if (address < item.Address)
180                  {
181                      right = middle - 1;
182                  }
183                  else
184                  {
185                      left = middle + 1;
186                  }
187              }
188  
189              return ~left;
190          }
191      }
192  }