Buffer.cs 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. using Ryujinx.Graphics.GAL;
  2. using System;
  3. namespace Ryujinx.Graphics.Gpu.Memory
  4. {
  5. /// <summary>
  6. /// Buffer, used to store vertex and index data, uniform and storage buffers, and others.
  7. /// </summary>
  8. class Buffer : IRange, IDisposable
  9. {
  10. private readonly GpuContext _context;
  11. /// <summary>
  12. /// Host buffer object.
  13. /// </summary>
  14. public IBuffer HostBuffer { get; }
  15. /// <summary>
  16. /// Start address of the buffer in guest memory.
  17. /// </summary>
  18. public ulong Address { get; }
  19. /// <summary>
  20. /// Size of the buffer in bytes.
  21. /// </summary>
  22. public ulong Size { get; }
  23. /// <summary>
  24. /// End address of the buffer in guest memory.
  25. /// </summary>
  26. public ulong EndAddress => Address + Size;
  27. private int[] _sequenceNumbers;
  28. /// <summary>
  29. /// Creates a new instance of the buffer.
  30. /// </summary>
  31. /// <param name="context">GPU context that the buffer belongs to</param>
  32. /// <param name="address">Start address of the buffer</param>
  33. /// <param name="size">Size of the buffer in bytes</param>
  34. public Buffer(GpuContext context, ulong address, ulong size)
  35. {
  36. _context = context;
  37. Address = address;
  38. Size = size;
  39. HostBuffer = context.Renderer.CreateBuffer((int)size);
  40. _sequenceNumbers = new int[size / MemoryManager.PageSize];
  41. Invalidate();
  42. }
  43. /// <summary>
  44. /// Gets a sub-range from the buffer.
  45. /// </summary>
  46. /// <remarks>
  47. /// This can be used to bind and use sub-ranges of the buffer on the host API.
  48. /// </remarks>
  49. /// <param name="address">Start address of the sub-range, must be greater than or equal to the buffer address</param>
  50. /// <param name="size">Size in bytes of the sub-range, must be less than or equal to the buffer size</param>
  51. /// <returns>The buffer sub-range</returns>
  52. public BufferRange GetRange(ulong address, ulong size)
  53. {
  54. int offset = (int)(address - Address);
  55. return new BufferRange(HostBuffer, offset, (int)size);
  56. }
  57. /// <summary>
  58. /// Checks if a given range overlaps with the buffer.
  59. /// </summary>
  60. /// <param name="address">Start address of the range</param>
  61. /// <param name="size">Size in bytes of the range</param>
  62. /// <returns>True if the range overlaps, false otherwise</returns>
  63. public bool OverlapsWith(ulong address, ulong size)
  64. {
  65. return Address < address + size && address < EndAddress;
  66. }
  67. /// <summary>
  68. /// Performs guest to host memory synchronization of the buffer data.
  69. /// </summary>
  70. /// <remarks>
  71. /// This causes the buffer data to be overwritten if a write was detected from the CPU,
  72. /// since the last call to this method.
  73. /// </remarks>
  74. /// <param name="address">Start address of the range to synchronize</param>
  75. /// <param name="size">Size in bytes of the range to synchronize</param>
  76. public void SynchronizeMemory(ulong address, ulong size)
  77. {
  78. int currentSequenceNumber = _context.SequenceNumber;
  79. bool needsSync = false;
  80. ulong buffOffset = address - Address;
  81. ulong buffEndOffset = (buffOffset + size + MemoryManager.PageMask) & ~MemoryManager.PageMask;
  82. int startIndex = (int)(buffOffset / MemoryManager.PageSize);
  83. int endIndex = (int)(buffEndOffset / MemoryManager.PageSize);
  84. for (int index = startIndex; index < endIndex; index++)
  85. {
  86. if (_sequenceNumbers[index] != currentSequenceNumber)
  87. {
  88. _sequenceNumbers[index] = currentSequenceNumber;
  89. needsSync = true;
  90. }
  91. }
  92. if (!needsSync)
  93. {
  94. return;
  95. }
  96. (ulong, ulong)[] modifiedRanges = _context.PhysicalMemory.GetModifiedRanges(address, size, ResourceName.Buffer);
  97. for (int index = 0; index < modifiedRanges.Length; index++)
  98. {
  99. (ulong mAddress, ulong mSize) = modifiedRanges[index];
  100. int offset = (int)(mAddress - Address);
  101. HostBuffer.SetData(offset, _context.PhysicalMemory.GetSpan(mAddress, mSize));
  102. }
  103. }
  104. /// <summary>
  105. /// Performs copy of all the buffer data from one buffer to another.
  106. /// </summary>
  107. /// <param name="destination">The destination buffer to copy the data into</param>
  108. /// <param name="dstOffset">The offset of the destination buffer to copy into</param>
  109. public void CopyTo(Buffer destination, int dstOffset)
  110. {
  111. HostBuffer.CopyTo(destination.HostBuffer, 0, dstOffset, (int)Size);
  112. }
  113. /// <summary>
  114. /// Flushes a range of the buffer.
  115. /// This writes the range data back into guest memory.
  116. /// </summary>
  117. /// <param name="address">Start address of the range</param>
  118. /// <param name="size">Size in bytes of the range</param>
  119. public void Flush(ulong address, ulong size)
  120. {
  121. int offset = (int)(address - Address);
  122. byte[] data = HostBuffer.GetData(offset, (int)size);
  123. _context.PhysicalMemory.Write(address, data);
  124. }
  125. /// <summary>
  126. /// Invalidates all the buffer data, causing it to be read from guest memory.
  127. /// </summary>
  128. public void Invalidate()
  129. {
  130. HostBuffer.SetData(0, _context.PhysicalMemory.GetSpan(Address, Size));
  131. }
  132. /// <summary>
  133. /// Disposes the host buffer.
  134. /// </summary>
  135. public void Dispose()
  136. {
  137. HostBuffer.Dispose();
  138. }
  139. }
  140. }