NvGpuVmmCache.cs 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. using ChocolArm64.Memory;
  2. using System;
  3. using System.Collections.Generic;
  4. namespace Ryujinx.Core.Gpu
  5. {
  6. class NvGpuVmmCache
  7. {
  8. private const int MaxCpCount = 10000;
  9. private const int MaxCpTimeDelta = 60000;
  10. private class CachedPage
  11. {
  12. private List<(long Start, long End)> Regions;
  13. public LinkedListNode<long> Node { get; set; }
  14. public int Count => Regions.Count;
  15. public int Timestamp { get; private set; }
  16. public long PABase { get; private set; }
  17. public NvGpuBufferType BufferType { get; private set; }
  18. public CachedPage(long PABase, NvGpuBufferType BufferType)
  19. {
  20. this.PABase = PABase;
  21. this.BufferType = BufferType;
  22. Regions = new List<(long, long)>();
  23. }
  24. public bool AddRange(long Start, long End)
  25. {
  26. for (int Index = 0; Index < Regions.Count; Index++)
  27. {
  28. (long RgStart, long RgEnd) = Regions[Index];
  29. if (Start >= RgStart && End <= RgEnd)
  30. {
  31. return false;
  32. }
  33. if (Start <= RgEnd && RgStart <= End)
  34. {
  35. long MinStart = Math.Min(RgStart, Start);
  36. long MaxEnd = Math.Max(RgEnd, End);
  37. Regions[Index] = (MinStart, MaxEnd);
  38. Timestamp = Environment.TickCount;
  39. return true;
  40. }
  41. }
  42. Regions.Add((Start, End));
  43. Timestamp = Environment.TickCount;
  44. return true;
  45. }
  46. }
  47. private Dictionary<long, CachedPage> Cache;
  48. private LinkedList<long> SortedCache;
  49. private int CpCount;
  50. public NvGpuVmmCache()
  51. {
  52. Cache = new Dictionary<long, CachedPage>();
  53. SortedCache = new LinkedList<long>();
  54. }
  55. public bool IsRegionModified(
  56. AMemory Memory,
  57. NvGpuBufferType BufferType,
  58. long VA,
  59. long PA,
  60. long Size)
  61. {
  62. ClearCachedPagesIfNeeded();
  63. long PageSize = Memory.GetHostPageSize();
  64. long Mask = PageSize - 1;
  65. long VAEnd = VA + Size;
  66. long PAEnd = PA + Size;
  67. bool RegMod = false;
  68. while (VA < VAEnd)
  69. {
  70. long Key = VA & ~Mask;
  71. long PABase = PA & ~Mask;
  72. long VAPgEnd = Math.Min((VA + PageSize) & ~Mask, VAEnd);
  73. long PAPgEnd = Math.Min((PA + PageSize) & ~Mask, PAEnd);
  74. bool IsCached = Cache.TryGetValue(Key, out CachedPage Cp);
  75. bool PgReset = false;
  76. if (!IsCached)
  77. {
  78. Cp = new CachedPage(PABase, BufferType);
  79. Cache.Add(Key, Cp);
  80. }
  81. else
  82. {
  83. CpCount -= Cp.Count;
  84. SortedCache.Remove(Cp.Node);
  85. if (Cp.PABase != PABase ||
  86. Cp.BufferType != BufferType)
  87. {
  88. PgReset = true;
  89. }
  90. }
  91. PgReset |= Memory.IsRegionModified(PA, PAPgEnd - PA) && IsCached;
  92. if (PgReset)
  93. {
  94. Cp = new CachedPage(PABase, BufferType);
  95. Cache[Key] = Cp;
  96. }
  97. Cp.Node = SortedCache.AddLast(Key);
  98. RegMod |= Cp.AddRange(VA, VAPgEnd);
  99. CpCount += Cp.Count;
  100. VA = VAPgEnd;
  101. PA = PAPgEnd;
  102. }
  103. return RegMod;
  104. }
  105. private void ClearCachedPagesIfNeeded()
  106. {
  107. if (CpCount <= MaxCpCount)
  108. {
  109. return;
  110. }
  111. int Timestamp = Environment.TickCount;
  112. int TimeDelta;
  113. do
  114. {
  115. if (!TryPopOldestCachedPageKey(Timestamp, out long Key))
  116. {
  117. break;
  118. }
  119. CachedPage Cp = Cache[Key];
  120. Cache.Remove(Key);
  121. CpCount -= Cp.Count;
  122. TimeDelta = RingDelta(Cp.Timestamp, Timestamp);
  123. }
  124. while (CpCount > (MaxCpCount >> 1) || (uint)TimeDelta > (uint)MaxCpTimeDelta);
  125. }
  126. private bool TryPopOldestCachedPageKey(int Timestamp, out long Key)
  127. {
  128. LinkedListNode<long> Node = SortedCache.First;
  129. if (Node == null)
  130. {
  131. Key = 0;
  132. return false;
  133. }
  134. SortedCache.Remove(Node);
  135. Key = Node.Value;
  136. return true;
  137. }
  138. private int RingDelta(int Old, int New)
  139. {
  140. if ((uint)New < (uint)Old)
  141. {
  142. return New + (~Old + 1);
  143. }
  144. else
  145. {
  146. return New - Old;
  147. }
  148. }
  149. }
  150. }