Buffer.cs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. using Ryujinx.Cpu.Tracking;
  2. using Ryujinx.Graphics.GAL;
  3. using Ryujinx.Memory.Range;
  4. using System;
  5. namespace Ryujinx.Graphics.Gpu.Memory
  6. {
  7. /// <summary>
  8. /// Buffer, used to store vertex and index data, uniform and storage buffers, and others.
  9. /// </summary>
  10. class Buffer : IRange, IDisposable
  11. {
  12. private static ulong GranularBufferThreshold = 4096;
  13. private readonly GpuContext _context;
  14. /// <summary>
  15. /// Host buffer handle.
  16. /// </summary>
  17. public BufferHandle Handle { get; }
  18. /// <summary>
  19. /// Start address of the buffer in guest memory.
  20. /// </summary>
  21. public ulong Address { get; }
  22. /// <summary>
  23. /// Size of the buffer in bytes.
  24. /// </summary>
  25. public ulong Size { get; }
  26. /// <summary>
  27. /// End address of the buffer in guest memory.
  28. /// </summary>
  29. public ulong EndAddress => Address + Size;
  30. private CpuMultiRegionHandle _memoryTrackingGranular;
  31. private CpuRegionHandle _memoryTracking;
  32. private readonly Action<ulong, ulong> _modifiedDelegate;
  33. private int _sequenceNumber;
  34. private bool _useGranular;
  35. /// <summary>
  36. /// Creates a new instance of the buffer.
  37. /// </summary>
  38. /// <param name="context">GPU context that the buffer belongs to</param>
  39. /// <param name="address">Start address of the buffer</param>
  40. /// <param name="size">Size of the buffer in bytes</param>
  41. public Buffer(GpuContext context, ulong address, ulong size)
  42. {
  43. _context = context;
  44. Address = address;
  45. Size = size;
  46. Handle = context.Renderer.CreateBuffer((int)size);
  47. _useGranular = size > GranularBufferThreshold;
  48. if (_useGranular)
  49. {
  50. _memoryTrackingGranular = context.PhysicalMemory.BeginGranularTracking(address, size);
  51. }
  52. else
  53. {
  54. _memoryTracking = context.PhysicalMemory.BeginTracking(address, size);
  55. }
  56. _modifiedDelegate = new Action<ulong, ulong>(RegionModified);
  57. }
  58. /// <summary>
  59. /// Gets a sub-range from the buffer.
  60. /// </summary>
  61. /// <remarks>
  62. /// This can be used to bind and use sub-ranges of the buffer on the host API.
  63. /// </remarks>
  64. /// <param name="address">Start address of the sub-range, must be greater than or equal to the buffer address</param>
  65. /// <param name="size">Size in bytes of the sub-range, must be less than or equal to the buffer size</param>
  66. /// <returns>The buffer sub-range</returns>
  67. public BufferRange GetRange(ulong address, ulong size)
  68. {
  69. int offset = (int)(address - Address);
  70. return new BufferRange(Handle, offset, (int)size);
  71. }
  72. /// <summary>
  73. /// Checks if a given range overlaps with the buffer.
  74. /// </summary>
  75. /// <param name="address">Start address of the range</param>
  76. /// <param name="size">Size in bytes of the range</param>
  77. /// <returns>True if the range overlaps, false otherwise</returns>
  78. public bool OverlapsWith(ulong address, ulong size)
  79. {
  80. return Address < address + size && address < EndAddress;
  81. }
  82. /// <summary>
  83. /// Performs guest to host memory synchronization of the buffer data.
  84. /// </summary>
  85. /// <remarks>
  86. /// This causes the buffer data to be overwritten if a write was detected from the CPU,
  87. /// since the last call to this method.
  88. /// </remarks>
  89. /// <param name="address">Start address of the range to synchronize</param>
  90. /// <param name="size">Size in bytes of the range to synchronize</param>
  91. public void SynchronizeMemory(ulong address, ulong size)
  92. {
  93. if (_useGranular)
  94. {
  95. _memoryTrackingGranular.QueryModified(address, size, _modifiedDelegate, _context.SequenceNumber);
  96. }
  97. else
  98. {
  99. if (_memoryTracking.Dirty && _context.SequenceNumber != _sequenceNumber)
  100. {
  101. _memoryTracking.Reprotect();
  102. _context.Renderer.SetBufferData(Handle, 0, _context.PhysicalMemory.GetSpan(Address, (int)Size));
  103. _sequenceNumber = _context.SequenceNumber;
  104. }
  105. }
  106. }
  107. /// <summary>
  108. /// Indicate that a region of the buffer was modified, and must be loaded from memory.
  109. /// </summary>
  110. /// <param name="mAddress">Start address of the modified region</param>
  111. /// <param name="mSize">Size of the modified region</param>
  112. private void RegionModified(ulong mAddress, ulong mSize)
  113. {
  114. if (mAddress < Address)
  115. {
  116. mAddress = Address;
  117. }
  118. ulong maxSize = Address + Size - mAddress;
  119. if (mSize > maxSize)
  120. {
  121. mSize = maxSize;
  122. }
  123. int offset = (int)(mAddress - Address);
  124. _context.Renderer.SetBufferData(Handle, offset, _context.PhysicalMemory.GetSpan(mAddress, (int)mSize));
  125. }
  126. /// <summary>
  127. /// Performs copy of all the buffer data from one buffer to another.
  128. /// </summary>
  129. /// <param name="destination">The destination buffer to copy the data into</param>
  130. /// <param name="dstOffset">The offset of the destination buffer to copy into</param>
  131. public void CopyTo(Buffer destination, int dstOffset)
  132. {
  133. _context.Renderer.Pipeline.CopyBuffer(Handle, destination.Handle, 0, dstOffset, (int)Size);
  134. }
  135. /// <summary>
  136. /// Flushes a range of the buffer.
  137. /// This writes the range data back into guest memory.
  138. /// </summary>
  139. /// <param name="address">Start address of the range</param>
  140. /// <param name="size">Size in bytes of the range</param>
  141. public void Flush(ulong address, ulong size)
  142. {
  143. int offset = (int)(address - Address);
  144. byte[] data = _context.Renderer.GetBufferData(Handle, offset, (int)size);
  145. // TODO: When write tracking shaders, they will need to be aware of changes in overlapping buffers.
  146. _context.PhysicalMemory.WriteUntracked(address, data);
  147. }
  148. /// <summary>
  149. /// Disposes the host buffer.
  150. /// </summary>
  151. public void Dispose()
  152. {
  153. _context.Renderer.DeleteBuffer(Handle);
  154. _memoryTrackingGranular?.Dispose();
  155. _memoryTracking?.Dispose();
  156. }
  157. }
  158. }