IbStreamer.cs 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. using Ryujinx.Common;
  2. using Ryujinx.Graphics.GAL;
  3. using System;
  4. using System.Runtime.InteropServices;
  5. namespace Ryujinx.Graphics.Gpu.Engine.Threed
  6. {
  7. /// <summary>
  8. /// Holds inline index buffer state.
  9. /// The inline index buffer data is sent to the GPU through the command buffer.
  10. /// </summary>
  11. struct IbStreamer
  12. {
  13. private const int BufferCapacity = 256; // Must be a power of 2.
  14. private BufferHandle _inlineIndexBuffer;
  15. private int _inlineIndexBufferSize;
  16. private int _inlineIndexCount;
  17. private uint[] _buffer;
  18. private int _bufferOffset;
  19. /// <summary>
  20. /// Indicates if any index buffer data has been pushed.
  21. /// </summary>
  22. public bool HasInlineIndexData => _inlineIndexCount != 0;
  23. /// <summary>
  24. /// Total numbers of indices that have been pushed.
  25. /// </summary>
  26. public int InlineIndexCount => _inlineIndexCount;
  27. /// <summary>
  28. /// Gets the handle for the host buffer currently holding the inline index buffer data.
  29. /// </summary>
  30. /// <returns>Host buffer handle</returns>
  31. public BufferHandle GetInlineIndexBuffer()
  32. {
  33. return _inlineIndexBuffer;
  34. }
  35. /// <summary>
  36. /// Gets the number of elements on the current inline index buffer,
  37. /// while also reseting it to zero for the next draw.
  38. /// </summary>
  39. /// <param name="renderer">Host renderer</param>
  40. /// <returns>Inline index bufffer count</returns>
  41. public int GetAndResetInlineIndexCount(IRenderer renderer)
  42. {
  43. UpdateRemaining(renderer);
  44. int temp = _inlineIndexCount;
  45. _inlineIndexCount = 0;
  46. return temp;
  47. }
  48. /// <summary>
  49. /// Pushes four 8-bit index buffer elements.
  50. /// </summary>
  51. /// <param name="renderer">Host renderer</param>
  52. /// <param name="argument">Method call argument</param>
  53. public void VbElementU8(IRenderer renderer, int argument)
  54. {
  55. byte i0 = (byte)argument;
  56. byte i1 = (byte)(argument >> 8);
  57. byte i2 = (byte)(argument >> 16);
  58. byte i3 = (byte)(argument >> 24);
  59. int offset = _inlineIndexCount;
  60. PushData(renderer, offset, i0);
  61. PushData(renderer, offset + 1, i1);
  62. PushData(renderer, offset + 2, i2);
  63. PushData(renderer, offset + 3, i3);
  64. _inlineIndexCount += 4;
  65. }
  66. /// <summary>
  67. /// Pushes two 16-bit index buffer elements.
  68. /// </summary>
  69. /// <param name="renderer">Host renderer</param>
  70. /// <param name="argument">Method call argument</param>
  71. public void VbElementU16(IRenderer renderer, int argument)
  72. {
  73. ushort i0 = (ushort)argument;
  74. ushort i1 = (ushort)(argument >> 16);
  75. int offset = _inlineIndexCount;
  76. PushData(renderer, offset, i0);
  77. PushData(renderer, offset + 1, i1);
  78. _inlineIndexCount += 2;
  79. }
  80. /// <summary>
  81. /// Pushes one 32-bit index buffer element.
  82. /// </summary>
  83. /// <param name="renderer">Host renderer</param>
  84. /// <param name="argument">Method call argument</param>
  85. public void VbElementU32(IRenderer renderer, int argument)
  86. {
  87. uint i0 = (uint)argument;
  88. int offset = _inlineIndexCount++;
  89. PushData(renderer, offset, i0);
  90. }
  91. /// <summary>
  92. /// Pushes a 32-bit value to the index buffer.
  93. /// </summary>
  94. /// <param name="renderer">Host renderer</param>
  95. /// <param name="offset">Offset where the data should be written, in 32-bit words</param>
  96. /// <param name="value">Index value to be written</param>
  97. private void PushData(IRenderer renderer, int offset, uint value)
  98. {
  99. if (_buffer == null)
  100. {
  101. _buffer = new uint[BufferCapacity];
  102. }
  103. // We upload data in chunks.
  104. // If we are at the start of a chunk, then the buffer might be full,
  105. // in that case we need to submit any existing data before overwriting the buffer.
  106. int subOffset = offset & (BufferCapacity - 1);
  107. if (subOffset == 0 && offset != 0)
  108. {
  109. int baseOffset = (offset - BufferCapacity) * sizeof(uint);
  110. BufferHandle buffer = GetInlineIndexBuffer(renderer, baseOffset, BufferCapacity * sizeof(uint));
  111. renderer.SetBufferData(buffer, baseOffset, MemoryMarshal.Cast<uint, byte>(_buffer));
  112. }
  113. _buffer[subOffset] = value;
  114. }
  115. /// <summary>
  116. /// Makes sure that any pending data is submitted to the GPU before the index buffer is used.
  117. /// </summary>
  118. /// <param name="renderer">Host renderer</param>
  119. private void UpdateRemaining(IRenderer renderer)
  120. {
  121. int offset = _inlineIndexCount;
  122. if (offset == 0)
  123. {
  124. return;
  125. }
  126. int count = offset & (BufferCapacity - 1);
  127. if (count == 0)
  128. {
  129. count = BufferCapacity;
  130. }
  131. int baseOffset = (offset - count) * sizeof(uint);
  132. int length = count * sizeof(uint);
  133. BufferHandle buffer = GetInlineIndexBuffer(renderer, baseOffset, length);
  134. renderer.SetBufferData(buffer, baseOffset, MemoryMarshal.Cast<uint, byte>(_buffer).Slice(0, length));
  135. }
  136. /// <summary>
  137. /// Gets the handle of a buffer large enough to hold the data that will be written to <paramref name="offset"/>.
  138. /// </summary>
  139. /// <param name="renderer">Host renderer</param>
  140. /// <param name="offset">Offset where the data will be written</param>
  141. /// <param name="length">Number of bytes that will be written</param>
  142. /// <returns>Buffer handle</returns>
  143. private BufferHandle GetInlineIndexBuffer(IRenderer renderer, int offset, int length)
  144. {
  145. // Calculate a reasonable size for the buffer that can fit all the data,
  146. // and that also won't require frequent resizes if we need to push more data.
  147. int size = BitUtils.AlignUp(offset + length + 0x10, 0x200);
  148. if (_inlineIndexBuffer == BufferHandle.Null)
  149. {
  150. _inlineIndexBuffer = renderer.CreateBuffer(size);
  151. _inlineIndexBufferSize = size;
  152. }
  153. else if (_inlineIndexBufferSize < size)
  154. {
  155. BufferHandle oldBuffer = _inlineIndexBuffer;
  156. int oldSize = _inlineIndexBufferSize;
  157. _inlineIndexBuffer = renderer.CreateBuffer(size);
  158. _inlineIndexBufferSize = size;
  159. renderer.Pipeline.CopyBuffer(oldBuffer, _inlineIndexBuffer, 0, 0, oldSize);
  160. renderer.DeleteBuffer(oldBuffer);
  161. }
  162. return _inlineIndexBuffer;
  163. }
  164. }
  165. }