StagingBuffer.cs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. namespace Ryujinx.Graphics.Vulkan
  5. {
  6. class StagingBuffer : IDisposable
  7. {
  8. private const int BufferSize = 16 * 1024 * 1024;
  9. private int _freeOffset;
  10. private int _freeSize;
  11. private readonly VulkanRenderer _gd;
  12. private readonly BufferHolder _buffer;
  13. private readonly struct PendingCopy
  14. {
  15. public FenceHolder Fence { get; }
  16. public int Size { get; }
  17. public PendingCopy(FenceHolder fence, int size)
  18. {
  19. Fence = fence;
  20. Size = size;
  21. fence.Get();
  22. }
  23. }
  24. private readonly Queue<PendingCopy> _pendingCopies;
  25. public StagingBuffer(VulkanRenderer gd, BufferManager bufferManager)
  26. {
  27. _gd = gd;
  28. _buffer = bufferManager.Create(gd, BufferSize);
  29. _pendingCopies = new Queue<PendingCopy>();
  30. _freeSize = BufferSize;
  31. }
  32. public unsafe void PushData(CommandBufferPool cbp, CommandBufferScoped? cbs, Action endRenderPass, BufferHolder dst, int dstOffset, ReadOnlySpan<byte> data)
  33. {
  34. bool isRender = cbs != null;
  35. CommandBufferScoped scoped = cbs ?? cbp.Rent();
  36. // Must push all data to the buffer. If it can't fit, split it up.
  37. endRenderPass?.Invoke();
  38. while (data.Length > 0)
  39. {
  40. if (_freeSize < data.Length)
  41. {
  42. FreeCompleted();
  43. }
  44. while (_freeSize == 0)
  45. {
  46. if (!WaitFreeCompleted(cbp))
  47. {
  48. if (isRender)
  49. {
  50. _gd.FlushAllCommands();
  51. scoped = cbp.Rent();
  52. isRender = false;
  53. }
  54. else
  55. {
  56. scoped = cbp.ReturnAndRent(scoped);
  57. }
  58. }
  59. }
  60. int chunkSize = Math.Min(_freeSize, data.Length);
  61. PushDataImpl(scoped, dst, dstOffset, data.Slice(0, chunkSize));
  62. dstOffset += chunkSize;
  63. data = data.Slice(chunkSize);
  64. }
  65. if (!isRender)
  66. {
  67. scoped.Dispose();
  68. }
  69. }
  70. private void PushDataImpl(CommandBufferScoped cbs, BufferHolder dst, int dstOffset, ReadOnlySpan<byte> data)
  71. {
  72. var srcBuffer = _buffer.GetBuffer();
  73. var dstBuffer = dst.GetBuffer(cbs.CommandBuffer, dstOffset, data.Length, true);
  74. int offset = _freeOffset;
  75. int capacity = BufferSize - offset;
  76. if (capacity < data.Length)
  77. {
  78. _buffer.SetDataUnchecked(offset, data.Slice(0, capacity));
  79. _buffer.SetDataUnchecked(0, data.Slice(capacity));
  80. BufferHolder.Copy(_gd, cbs, srcBuffer, dstBuffer, offset, dstOffset, capacity);
  81. BufferHolder.Copy(_gd, cbs, srcBuffer, dstBuffer, 0, dstOffset + capacity, data.Length - capacity);
  82. }
  83. else
  84. {
  85. _buffer.SetDataUnchecked(offset, data);
  86. BufferHolder.Copy(_gd, cbs, srcBuffer, dstBuffer, offset, dstOffset, data.Length);
  87. }
  88. _freeOffset = (offset + data.Length) & (BufferSize - 1);
  89. _freeSize -= data.Length;
  90. Debug.Assert(_freeSize >= 0);
  91. _pendingCopies.Enqueue(new PendingCopy(cbs.GetFence(), data.Length));
  92. }
  93. public unsafe bool TryPushData(CommandBufferScoped cbs, Action endRenderPass, BufferHolder dst, int dstOffset, ReadOnlySpan<byte> data)
  94. {
  95. if (data.Length > BufferSize)
  96. {
  97. return false;
  98. }
  99. if (_freeSize < data.Length)
  100. {
  101. FreeCompleted();
  102. if (_freeSize < data.Length)
  103. {
  104. return false;
  105. }
  106. }
  107. endRenderPass();
  108. PushDataImpl(cbs, dst, dstOffset, data);
  109. return true;
  110. }
  111. private bool WaitFreeCompleted(CommandBufferPool cbp)
  112. {
  113. if (_pendingCopies.TryPeek(out var pc))
  114. {
  115. if (!pc.Fence.IsSignaled())
  116. {
  117. if (cbp.IsFenceOnRentedCommandBuffer(pc.Fence))
  118. {
  119. return false;
  120. }
  121. pc.Fence.Wait();
  122. }
  123. var dequeued = _pendingCopies.Dequeue();
  124. Debug.Assert(dequeued.Fence == pc.Fence);
  125. _freeSize += pc.Size;
  126. pc.Fence.Put();
  127. }
  128. return true;
  129. }
  130. private void FreeCompleted()
  131. {
  132. FenceHolder signalledFence = null;
  133. while (_pendingCopies.TryPeek(out var pc) && (pc.Fence == signalledFence || pc.Fence.IsSignaled()))
  134. {
  135. signalledFence = pc.Fence; // Already checked - don't need to do it again.
  136. var dequeued = _pendingCopies.Dequeue();
  137. Debug.Assert(dequeued.Fence == pc.Fence);
  138. _freeSize += pc.Size;
  139. pc.Fence.Put();
  140. }
  141. }
  142. protected virtual void Dispose(bool disposing)
  143. {
  144. if (disposing)
  145. {
  146. _buffer.Dispose();
  147. while (_pendingCopies.TryDequeue(out var pc))
  148. {
  149. pc.Fence.Put();
  150. }
  151. }
  152. }
  153. public void Dispose()
  154. {
  155. Dispose(true);
  156. }
  157. }
  158. }