MacroHLE.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  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 const int IndirectDataEntrySize = 0x10;
  18. private const int IndirectIndexedDataEntrySize = 0x14;
  19. private readonly GPFifoProcessor _processor;
  20. private readonly MacroHLEFunctionName _functionName;
  21. /// <summary>
  22. /// Arguments FIFO.
  23. /// </summary>
  24. public Queue<FifoWord> Fifo { get; }
  25. /// <summary>
  26. /// Creates a new instance of the HLE macro handler.
  27. /// </summary>
  28. /// <param name="processor">GPU GP FIFO command processor</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.DrawArraysInstanced:
  53. DrawArraysInstanced(state, arg0);
  54. break;
  55. case MacroHLEFunctionName.DrawElementsInstanced:
  56. DrawElementsInstanced(state, arg0);
  57. break;
  58. case MacroHLEFunctionName.DrawElementsIndirect:
  59. DrawElementsIndirect(state, arg0);
  60. break;
  61. case MacroHLEFunctionName.MultiDrawElementsIndirectCount:
  62. MultiDrawElementsIndirectCount(state, arg0);
  63. break;
  64. default:
  65. throw new NotImplementedException(_functionName.ToString());
  66. }
  67. // It should be empty at this point, but clear it just to be safe.
  68. Fifo.Clear();
  69. }
  70. /// <summary>
  71. /// Clears one bound color 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 ClearColor(IDeviceState state, int arg0)
  76. {
  77. int index = (arg0 >> 6) & 0xf;
  78. int layerCount = state.Read(ColorLayerCountOffset + index * ColorStructSize);
  79. _processor.ThreedClass.Clear(arg0, layerCount);
  80. }
  81. /// <summary>
  82. /// Clears the current depth-stencil target.
  83. /// </summary>
  84. /// <param name="state">GPU state at the time of the call</param>
  85. /// <param name="arg0">First argument of the call</param>
  86. private void ClearDepthStencil(IDeviceState state, int arg0)
  87. {
  88. int layerCount = state.Read(ZetaLayerCountOffset);
  89. _processor.ThreedClass.Clear(arg0, layerCount);
  90. }
  91. /// <summary>
  92. /// Performs a draw.
  93. /// </summary>
  94. /// <param name="state">GPU state at the time of the call</param>
  95. /// <param name="arg0">First argument of the call</param>
  96. private void DrawArraysInstanced(IDeviceState state, int arg0)
  97. {
  98. var topology = (PrimitiveTopology)arg0;
  99. var count = FetchParam();
  100. var instanceCount = FetchParam();
  101. var firstVertex = FetchParam();
  102. var firstInstance = FetchParam();
  103. if (ShouldSkipDraw(state, instanceCount.Word))
  104. {
  105. return;
  106. }
  107. _processor.ThreedClass.Draw(
  108. topology,
  109. count.Word,
  110. instanceCount.Word,
  111. 0,
  112. firstVertex.Word,
  113. firstInstance.Word,
  114. indexed: false);
  115. }
  116. /// <summary>
  117. /// Performs a indexed draw.
  118. /// </summary>
  119. /// <param name="state">GPU state at the time of the call</param>
  120. /// <param name="arg0">First argument of the call</param>
  121. private void DrawElementsInstanced(IDeviceState state, int arg0)
  122. {
  123. var topology = (PrimitiveTopology)arg0;
  124. var count = FetchParam();
  125. var instanceCount = FetchParam();
  126. var firstIndex = FetchParam();
  127. var firstVertex = FetchParam();
  128. var firstInstance = FetchParam();
  129. if (ShouldSkipDraw(state, instanceCount.Word))
  130. {
  131. return;
  132. }
  133. _processor.ThreedClass.Draw(
  134. topology,
  135. count.Word,
  136. instanceCount.Word,
  137. firstIndex.Word,
  138. firstVertex.Word,
  139. firstInstance.Word,
  140. indexed: true);
  141. }
  142. /// <summary>
  143. /// Performs a indirect indexed draw, with parameters from a GPU buffer.
  144. /// </summary>
  145. /// <param name="state">GPU state at the time of the call</param>
  146. /// <param name="arg0">First argument of the call</param>
  147. private void DrawElementsIndirect(IDeviceState state, int arg0)
  148. {
  149. var topology = (PrimitiveTopology)arg0;
  150. var count = FetchParam();
  151. var instanceCount = FetchParam();
  152. var firstIndex = FetchParam();
  153. var firstVertex = FetchParam();
  154. var firstInstance = FetchParam();
  155. ulong indirectBufferGpuVa = count.GpuVa;
  156. var bufferCache = _processor.MemoryManager.Physical.BufferCache;
  157. bool useBuffer = bufferCache.CheckModified(_processor.MemoryManager, indirectBufferGpuVa, IndirectIndexedDataEntrySize, out ulong indirectBufferAddress);
  158. if (useBuffer)
  159. {
  160. int indexCount = firstIndex.Word + count.Word;
  161. _processor.ThreedClass.DrawIndirect(
  162. topology,
  163. indirectBufferAddress,
  164. 0,
  165. 1,
  166. IndirectIndexedDataEntrySize,
  167. indexCount,
  168. Threed.IndirectDrawType.DrawIndexedIndirect);
  169. }
  170. else
  171. {
  172. if (ShouldSkipDraw(state, instanceCount.Word))
  173. {
  174. return;
  175. }
  176. _processor.ThreedClass.Draw(
  177. topology,
  178. count.Word,
  179. instanceCount.Word,
  180. firstIndex.Word,
  181. firstVertex.Word,
  182. firstInstance.Word,
  183. indexed: true);
  184. }
  185. }
  186. /// <summary>
  187. /// Performs a indirect indexed multi-draw, with parameters from a GPU buffer.
  188. /// </summary>
  189. /// <param name="state">GPU state at the time of the call</param>
  190. /// <param name="arg0">First argument of the call</param>
  191. private void MultiDrawElementsIndirectCount(IDeviceState state, int arg0)
  192. {
  193. int arg1 = FetchParam().Word;
  194. int arg2 = FetchParam().Word;
  195. int arg3 = FetchParam().Word;
  196. int startDraw = arg0;
  197. int endDraw = arg1;
  198. var topology = (PrimitiveTopology)arg2;
  199. int paddingWords = arg3;
  200. int stride = paddingWords * 4 + 0x14;
  201. ulong parameterBufferGpuVa = FetchParam().GpuVa;
  202. int maxDrawCount = endDraw - startDraw;
  203. if (startDraw != 0)
  204. {
  205. int drawCount = _processor.MemoryManager.Read<int>(parameterBufferGpuVa, tracked: true);
  206. // Calculate maximum draw count based on the previous draw count and current draw count.
  207. if ((uint)drawCount <= (uint)startDraw)
  208. {
  209. // The start draw is past our total draw count, so all draws were already performed.
  210. maxDrawCount = 0;
  211. }
  212. else
  213. {
  214. // Perform just the missing number of draws.
  215. maxDrawCount = (int)Math.Min((uint)maxDrawCount, (uint)(drawCount - startDraw));
  216. }
  217. }
  218. if (maxDrawCount == 0)
  219. {
  220. Fifo.Clear();
  221. return;
  222. }
  223. ulong indirectBufferGpuVa = 0;
  224. int indexCount = 0;
  225. for (int i = 0; i < maxDrawCount; i++)
  226. {
  227. var count = FetchParam();
  228. var instanceCount = FetchParam();
  229. var firstIndex = FetchParam();
  230. var firstVertex = FetchParam();
  231. var firstInstance = FetchParam();
  232. if (i == 0)
  233. {
  234. indirectBufferGpuVa = count.GpuVa;
  235. }
  236. indexCount = Math.Max(indexCount, count.Word + firstIndex.Word);
  237. if (i != maxDrawCount - 1)
  238. {
  239. for (int j = 0; j < paddingWords; j++)
  240. {
  241. FetchParam();
  242. }
  243. }
  244. }
  245. var bufferCache = _processor.MemoryManager.Physical.BufferCache;
  246. ulong indirectBufferSize = (ulong)maxDrawCount * (ulong)stride;
  247. ulong indirectBufferAddress = bufferCache.TranslateAndCreateBuffer(_processor.MemoryManager, indirectBufferGpuVa, indirectBufferSize);
  248. ulong parameterBufferAddress = bufferCache.TranslateAndCreateBuffer(_processor.MemoryManager, parameterBufferGpuVa, 4);
  249. _processor.ThreedClass.DrawIndirect(
  250. topology,
  251. indirectBufferAddress,
  252. parameterBufferAddress,
  253. maxDrawCount,
  254. stride,
  255. indexCount,
  256. Threed.IndirectDrawType.DrawIndexedIndirectCount);
  257. }
  258. /// <summary>
  259. /// Checks if the draw should be skipped, because the masked instance count is zero.
  260. /// </summary>
  261. /// <param name="state">Current GPU state</param>
  262. /// <param name="instanceCount">Draw instance count</param>
  263. /// <returns>True if the draw should be skipped, false otherwise</returns>
  264. private static bool ShouldSkipDraw(IDeviceState state, int instanceCount)
  265. {
  266. return (Read(state, 0xd1b) & instanceCount) == 0;
  267. }
  268. /// <summary>
  269. /// Fetches a arguments from the arguments FIFO.
  270. /// </summary>
  271. /// <returns>The call argument, or a 0 value with null address if the FIFO is empty</returns>
  272. private FifoWord FetchParam()
  273. {
  274. if (!Fifo.TryDequeue(out var value))
  275. {
  276. Logger.Warning?.Print(LogClass.Gpu, "Macro attempted to fetch an inexistent argument.");
  277. return new FifoWord(0UL, 0);
  278. }
  279. return value;
  280. }
  281. /// <summary>
  282. /// Reads data from a GPU register.
  283. /// </summary>
  284. /// <param name="state">Current GPU state</param>
  285. /// <param name="reg">Register offset to read</param>
  286. /// <returns>GPU register value</returns>
  287. private static int Read(IDeviceState state, int reg)
  288. {
  289. return state.Read(reg * 4);
  290. }
  291. }
  292. }