MacroHLE.cs 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. using Ryujinx.Common.Logging;
  2. using Ryujinx.Graphics.Device;
  3. using Ryujinx.Graphics.GAL;
  4. using Ryujinx.Graphics.Gpu.Engine.GPFifo;
  5. using System;
  6. using System.Collections.Generic;
  7. namespace Ryujinx.Graphics.Gpu.Engine.MME
  8. {
  9. /// <summary>
  10. /// Macro High-level emulation.
  11. /// </summary>
  12. class MacroHLE : IMacroEE
  13. {
  14. private const int ColorLayerCountOffset = 0x818;
  15. private const int ColorStructSize = 0x40;
  16. private const int ZetaLayerCountOffset = 0x1230;
  17. private readonly GPFifoProcessor _processor;
  18. private readonly MacroHLEFunctionName _functionName;
  19. /// <summary>
  20. /// Arguments FIFO.
  21. /// </summary>
  22. public Queue<FifoWord> Fifo { get; }
  23. /// <summary>
  24. /// Creates a new instance of the HLE macro handler.
  25. /// </summary>
  26. /// <param name="context">GPU context the macro is being executed on</param>
  27. /// <param name="memoryManager">GPU memory manager</param>
  28. /// <param name="engine">3D engine where this macro is being called</param>
  29. /// <param name="functionName">Name of the HLE macro function to be called</param>
  30. public MacroHLE(GPFifoProcessor processor, MacroHLEFunctionName functionName)
  31. {
  32. _processor = processor;
  33. _functionName = functionName;
  34. Fifo = new Queue<FifoWord>();
  35. }
  36. /// <summary>
  37. /// Executes a macro program until it exits.
  38. /// </summary>
  39. /// <param name="code">Code of the program to execute</param>
  40. /// <param name="state">GPU state at the time of the call</param>
  41. /// <param name="arg0">Optional argument passed to the program, 0 if not used</param>
  42. public void Execute(ReadOnlySpan<int> code, IDeviceState state, int arg0)
  43. {
  44. switch (_functionName)
  45. {
  46. case MacroHLEFunctionName.ClearColor:
  47. ClearColor(state, arg0);
  48. break;
  49. case MacroHLEFunctionName.ClearDepthStencil:
  50. ClearDepthStencil(state, arg0);
  51. break;
  52. case MacroHLEFunctionName.MultiDrawElementsIndirectCount:
  53. MultiDrawElementsIndirectCount(state, arg0);
  54. break;
  55. default:
  56. throw new NotImplementedException(_functionName.ToString());
  57. }
  58. }
  59. /// <summary>
  60. /// Clears one bound color target.
  61. /// </summary>
  62. /// <param name="state">GPU state at the time of the call</param>
  63. /// <param name="arg0">First argument of the call</param>
  64. private void ClearColor(IDeviceState state, int arg0)
  65. {
  66. int index = (arg0 >> 6) & 0xf;
  67. int layerCount = state.Read(ColorLayerCountOffset + index * ColorStructSize);
  68. _processor.ThreedClass.Clear(arg0, layerCount);
  69. }
  70. /// <summary>
  71. /// Clears the current depth-stencil target.
  72. /// </summary>
  73. /// <param name="state">GPU state at the time of the call</param>
  74. /// <param name="arg0">First argument of the call</param>
  75. private void ClearDepthStencil(IDeviceState state, int arg0)
  76. {
  77. int layerCount = state.Read(ZetaLayerCountOffset);
  78. _processor.ThreedClass.Clear(arg0, layerCount);
  79. }
  80. /// <summary>
  81. /// Performs a indirect multi-draw, with parameters from a GPU buffer.
  82. /// </summary>
  83. /// <param name="state">GPU state at the time of the call</param>
  84. /// <param name="arg0">First argument of the call</param>
  85. private void MultiDrawElementsIndirectCount(IDeviceState state, int arg0)
  86. {
  87. int arg1 = FetchParam().Word;
  88. int arg2 = FetchParam().Word;
  89. int arg3 = FetchParam().Word;
  90. int startDraw = arg0;
  91. int endDraw = arg1;
  92. var topology = (PrimitiveTopology)arg2;
  93. int paddingWords = arg3;
  94. int stride = paddingWords * 4 + 0x14;
  95. ulong parameterBufferGpuVa = FetchParam().GpuVa;
  96. int maxDrawCount = endDraw - startDraw;
  97. if (startDraw != 0)
  98. {
  99. int drawCount = _processor.MemoryManager.Read<int>(parameterBufferGpuVa, tracked: true);
  100. // Calculate maximum draw count based on the previous draw count and current draw count.
  101. if ((uint)drawCount <= (uint)startDraw)
  102. {
  103. // The start draw is past our total draw count, so all draws were already performed.
  104. maxDrawCount = 0;
  105. }
  106. else
  107. {
  108. // Perform just the missing number of draws.
  109. maxDrawCount = (int)Math.Min((uint)maxDrawCount, (uint)(drawCount - startDraw));
  110. }
  111. }
  112. if (maxDrawCount == 0)
  113. {
  114. Fifo.Clear();
  115. return;
  116. }
  117. int indirectBufferSize = maxDrawCount * stride;
  118. ulong indirectBufferGpuVa = 0;
  119. int indexCount = 0;
  120. for (int i = 0; i < maxDrawCount; i++)
  121. {
  122. var count = FetchParam();
  123. var instanceCount = FetchParam();
  124. var firstIndex = FetchParam();
  125. var baseVertex = FetchParam();
  126. var baseInstance = FetchParam();
  127. if (i == 0)
  128. {
  129. indirectBufferGpuVa = count.GpuVa;
  130. }
  131. indexCount = Math.Max(indexCount, count.Word + firstIndex.Word);
  132. if (i != maxDrawCount - 1)
  133. {
  134. for (int j = 0; j < paddingWords; j++)
  135. {
  136. FetchParam();
  137. }
  138. }
  139. }
  140. // It should be empty at this point, but clear it just to be safe.
  141. Fifo.Clear();
  142. var bufferCache = _processor.MemoryManager.Physical.BufferCache;
  143. var parameterBuffer = bufferCache.GetGpuBufferRange(_processor.MemoryManager, parameterBufferGpuVa, 4);
  144. var indirectBuffer = bufferCache.GetGpuBufferRange(_processor.MemoryManager, indirectBufferGpuVa, (ulong)indirectBufferSize);
  145. _processor.ThreedClass.MultiDrawIndirectCount(indexCount, topology, indirectBuffer, parameterBuffer, maxDrawCount, stride);
  146. }
  147. /// <summary>
  148. /// Fetches a arguments from the arguments FIFO.
  149. /// </summary>
  150. /// <returns>The call argument, or a 0 value with null address if the FIFO is empty</returns>
  151. private FifoWord FetchParam()
  152. {
  153. if (!Fifo.TryDequeue(out var value))
  154. {
  155. Logger.Warning?.Print(LogClass.Gpu, "Macro attempted to fetch an inexistent argument.");
  156. return new FifoWord(0UL, 0);
  157. }
  158. return value;
  159. }
  160. /// <summary>
  161. /// Performs a GPU method call.
  162. /// </summary>
  163. /// <param name="state">Current GPU state</param>
  164. /// <param name="methAddr">Address, in words, of the method</param>
  165. /// <param name="value">Call argument</param>
  166. private static void Send(IDeviceState state, int methAddr, int value)
  167. {
  168. state.Write(methAddr * 4, value);
  169. }
  170. }
  171. }