Buffer.cs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  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 handle.
  13. /// </summary>
  14. public BufferHandle Handle { 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 readonly (ulong, ulong)[] _modifiedRanges;
  28. private readonly int[] _sequenceNumbers;
  29. /// <summary>
  30. /// Creates a new instance of the buffer.
  31. /// </summary>
  32. /// <param name="context">GPU context that the buffer belongs to</param>
  33. /// <param name="address">Start address of the buffer</param>
  34. /// <param name="size">Size of the buffer in bytes</param>
  35. public Buffer(GpuContext context, ulong address, ulong size)
  36. {
  37. _context = context;
  38. Address = address;
  39. Size = size;
  40. Handle = context.Renderer.CreateBuffer((int)size);
  41. _modifiedRanges = new (ulong, ulong)[size / PhysicalMemory.PageSize];
  42. _sequenceNumbers = new int[size / MemoryManager.PageSize];
  43. }
  44. /// <summary>
  45. /// Gets a sub-range from the buffer.
  46. /// </summary>
  47. /// <remarks>
  48. /// This can be used to bind and use sub-ranges of the buffer on the host API.
  49. /// </remarks>
  50. /// <param name="address">Start address of the sub-range, must be greater than or equal to the buffer address</param>
  51. /// <param name="size">Size in bytes of the sub-range, must be less than or equal to the buffer size</param>
  52. /// <returns>The buffer sub-range</returns>
  53. public BufferRange GetRange(ulong address, ulong size)
  54. {
  55. int offset = (int)(address - Address);
  56. return new BufferRange(Handle, offset, (int)size);
  57. }
  58. /// <summary>
  59. /// Checks if a given range overlaps with the buffer.
  60. /// </summary>
  61. /// <param name="address">Start address of the range</param>
  62. /// <param name="size">Size in bytes of the range</param>
  63. /// <returns>True if the range overlaps, false otherwise</returns>
  64. public bool OverlapsWith(ulong address, ulong size)
  65. {
  66. return Address < address + size && address < EndAddress;
  67. }
  68. /// <summary>
  69. /// Performs guest to host memory synchronization of the buffer data.
  70. /// </summary>
  71. /// <remarks>
  72. /// This causes the buffer data to be overwritten if a write was detected from the CPU,
  73. /// since the last call to this method.
  74. /// </remarks>
  75. /// <param name="address">Start address of the range to synchronize</param>
  76. /// <param name="size">Size in bytes of the range to synchronize</param>
  77. public void SynchronizeMemory(ulong address, ulong size)
  78. {
  79. int currentSequenceNumber = _context.SequenceNumber;
  80. bool needsSync = false;
  81. ulong buffOffset = address - Address;
  82. ulong buffEndOffset = (buffOffset + size + MemoryManager.PageMask) & ~MemoryManager.PageMask;
  83. int startIndex = (int)(buffOffset / MemoryManager.PageSize);
  84. int endIndex = (int)(buffEndOffset / MemoryManager.PageSize);
  85. for (int index = startIndex; index < endIndex; index++)
  86. {
  87. if (_sequenceNumbers[index] != currentSequenceNumber)
  88. {
  89. _sequenceNumbers[index] = currentSequenceNumber;
  90. needsSync = true;
  91. }
  92. }
  93. if (!needsSync)
  94. {
  95. return;
  96. }
  97. int count = _context.PhysicalMemory.QueryModified(address, size, ResourceName.Buffer, _modifiedRanges);
  98. for (int index = 0; index < count; index++)
  99. {
  100. (ulong mAddress, ulong mSize) = _modifiedRanges[index];
  101. int offset = (int)(mAddress - Address);
  102. _context.Renderer.SetBufferData(Handle, offset, _context.PhysicalMemory.GetSpan(mAddress, (int)mSize));
  103. }
  104. }
  105. /// <summary>
  106. /// Performs copy of all the buffer data from one buffer to another.
  107. /// </summary>
  108. /// <param name="destination">The destination buffer to copy the data into</param>
  109. /// <param name="dstOffset">The offset of the destination buffer to copy into</param>
  110. public void CopyTo(Buffer destination, int dstOffset)
  111. {
  112. _context.Renderer.Pipeline.CopyBuffer(Handle, destination.Handle, 0, dstOffset, (int)Size);
  113. }
  114. /// <summary>
  115. /// Flushes a range of the buffer.
  116. /// This writes the range data back into guest memory.
  117. /// </summary>
  118. /// <param name="address">Start address of the range</param>
  119. /// <param name="size">Size in bytes of the range</param>
  120. public void Flush(ulong address, ulong size)
  121. {
  122. int offset = (int)(address - Address);
  123. byte[] data = _context.Renderer.GetBufferData(Handle, offset, (int)size);
  124. _context.PhysicalMemory.Write(address, data);
  125. }
  126. /// <summary>
  127. /// Disposes the host buffer.
  128. /// </summary>
  129. public void Dispose()
  130. {
  131. _context.Renderer.DeleteBuffer(Handle);
  132. }
  133. }
  134. }