Buffer.cs 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  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 CpuSmartMultiRegionHandle _memoryTrackingGranular;
  31. private CpuRegionHandle _memoryTracking;
  32. private int _sequenceNumber;
  33. private bool _useGranular;
  34. /// <summary>
  35. /// Creates a new instance of the buffer.
  36. /// </summary>
  37. /// <param name="context">GPU context that the buffer belongs to</param>
  38. /// <param name="address">Start address of the buffer</param>
  39. /// <param name="size">Size of the buffer in bytes</param>
  40. public Buffer(GpuContext context, ulong address, ulong size)
  41. {
  42. _context = context;
  43. Address = address;
  44. Size = size;
  45. Handle = context.Renderer.CreateBuffer((int)size);
  46. _useGranular = size > GranularBufferThreshold;
  47. if (_useGranular)
  48. {
  49. _memoryTrackingGranular = context.PhysicalMemory.BeginSmartGranularTracking(address, size);
  50. }
  51. else
  52. {
  53. _memoryTracking = context.PhysicalMemory.BeginTracking(address, size);
  54. }
  55. }
  56. /// <summary>
  57. /// Gets a sub-range from the buffer.
  58. /// </summary>
  59. /// <remarks>
  60. /// This can be used to bind and use sub-ranges of the buffer on the host API.
  61. /// </remarks>
  62. /// <param name="address">Start address of the sub-range, must be greater than or equal to the buffer address</param>
  63. /// <param name="size">Size in bytes of the sub-range, must be less than or equal to the buffer size</param>
  64. /// <returns>The buffer sub-range</returns>
  65. public BufferRange GetRange(ulong address, ulong size)
  66. {
  67. int offset = (int)(address - Address);
  68. return new BufferRange(Handle, offset, (int)size);
  69. }
  70. /// <summary>
  71. /// Checks if a given range overlaps with the buffer.
  72. /// </summary>
  73. /// <param name="address">Start address of the range</param>
  74. /// <param name="size">Size in bytes of the range</param>
  75. /// <returns>True if the range overlaps, false otherwise</returns>
  76. public bool OverlapsWith(ulong address, ulong size)
  77. {
  78. return Address < address + size && address < EndAddress;
  79. }
  80. /// <summary>
  81. /// Performs guest to host memory synchronization of the buffer data.
  82. /// </summary>
  83. /// <remarks>
  84. /// This causes the buffer data to be overwritten if a write was detected from the CPU,
  85. /// since the last call to this method.
  86. /// </remarks>
  87. /// <param name="address">Start address of the range to synchronize</param>
  88. /// <param name="size">Size in bytes of the range to synchronize</param>
  89. public void SynchronizeMemory(ulong address, ulong size)
  90. {
  91. if (_useGranular)
  92. {
  93. _memoryTrackingGranular.QueryModified(address, size, (ulong mAddress, ulong mSize) =>
  94. {
  95. if (mAddress < Address)
  96. {
  97. mAddress = Address;
  98. }
  99. ulong maxSize = Address + Size - mAddress;
  100. if (mSize > maxSize)
  101. {
  102. mSize = maxSize;
  103. }
  104. int offset = (int)(mAddress - Address);
  105. _context.Renderer.SetBufferData(Handle, offset, _context.PhysicalMemory.GetSpan(mAddress, (int)mSize));
  106. }, _context.SequenceNumber);
  107. }
  108. else
  109. {
  110. if (_memoryTracking.Dirty && _context.SequenceNumber != _sequenceNumber)
  111. {
  112. _memoryTracking.Reprotect();
  113. _context.Renderer.SetBufferData(Handle, 0, _context.PhysicalMemory.GetSpan(Address, (int)Size));
  114. _sequenceNumber = _context.SequenceNumber;
  115. }
  116. }
  117. }
  118. /// <summary>
  119. /// Performs copy of all the buffer data from one buffer to another.
  120. /// </summary>
  121. /// <param name="destination">The destination buffer to copy the data into</param>
  122. /// <param name="dstOffset">The offset of the destination buffer to copy into</param>
  123. public void CopyTo(Buffer destination, int dstOffset)
  124. {
  125. _context.Renderer.Pipeline.CopyBuffer(Handle, destination.Handle, 0, dstOffset, (int)Size);
  126. }
  127. /// <summary>
  128. /// Flushes a range of the buffer.
  129. /// This writes the range data back into guest memory.
  130. /// </summary>
  131. /// <param name="address">Start address of the range</param>
  132. /// <param name="size">Size in bytes of the range</param>
  133. public void Flush(ulong address, ulong size)
  134. {
  135. int offset = (int)(address - Address);
  136. byte[] data = _context.Renderer.GetBufferData(Handle, offset, (int)size);
  137. // TODO: When write tracking shaders, they will need to be aware of changes in overlapping buffers.
  138. _context.PhysicalMemory.WriteUntracked(address, data);
  139. }
  140. /// <summary>
  141. /// Disposes the host buffer.
  142. /// </summary>
  143. public void Dispose()
  144. {
  145. _context.Renderer.DeleteBuffer(Handle);
  146. _memoryTrackingGranular?.Dispose();
  147. _memoryTracking?.Dispose();
  148. }
  149. }
  150. }