MacroHLE.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. using Ryujinx.Common.Logging;
  2. using Ryujinx.Common.Memory;
  3. using Ryujinx.Graphics.Device;
  4. using Ryujinx.Graphics.GAL;
  5. using Ryujinx.Graphics.Gpu.Engine.GPFifo;
  6. using Ryujinx.Graphics.Gpu.Engine.Threed;
  7. using Ryujinx.Graphics.Gpu.Engine.Types;
  8. using Ryujinx.Graphics.Gpu.Memory;
  9. using Ryujinx.Memory.Range;
  10. using System;
  11. using System.Collections.Generic;
  12. namespace Ryujinx.Graphics.Gpu.Engine.MME
  13. {
  14. /// <summary>
  15. /// Macro High-level emulation.
  16. /// </summary>
  17. class MacroHLE : IMacroEE
  18. {
  19. private const int ColorLayerCountOffset = 0x818;
  20. private const int ColorStructSize = 0x40;
  21. private const int ZetaLayerCountOffset = 0x1230;
  22. private const int UniformBufferBindVertexOffset = 0x2410;
  23. private const int FirstVertexOffset = 0x1434;
  24. private const int IndirectIndexedDataEntrySize = 0x14;
  25. private const int LogicOpOffset = 0x19c4;
  26. private const int ShaderIdScratchOffset = 0x3470;
  27. private const int ShaderAddressScratchOffset = 0x3488;
  28. private const int UpdateConstantBufferAddressesBase = 0x34a8;
  29. private const int UpdateConstantBufferSizesBase = 0x34bc;
  30. private const int UpdateConstantBufferAddressCbu = 0x3460;
  31. private readonly GPFifoProcessor _processor;
  32. private readonly MacroHLEFunctionName _functionName;
  33. /// <summary>
  34. /// Arguments FIFO.
  35. /// </summary>
  36. public Queue<FifoWord> Fifo { get; }
  37. /// <summary>
  38. /// Creates a new instance of the HLE macro handler.
  39. /// </summary>
  40. /// <param name="processor">GPU GP FIFO command processor</param>
  41. /// <param name="functionName">Name of the HLE macro function to be called</param>
  42. public MacroHLE(GPFifoProcessor processor, MacroHLEFunctionName functionName)
  43. {
  44. _processor = processor;
  45. _functionName = functionName;
  46. Fifo = new Queue<FifoWord>();
  47. }
  48. /// <summary>
  49. /// Executes a macro program until it exits.
  50. /// </summary>
  51. /// <param name="code">Code of the program to execute</param>
  52. /// <param name="state">GPU state at the time of the call</param>
  53. /// <param name="arg0">Optional argument passed to the program, 0 if not used</param>
  54. public void Execute(ReadOnlySpan<int> code, IDeviceState state, int arg0)
  55. {
  56. switch (_functionName)
  57. {
  58. case MacroHLEFunctionName.BindShaderProgram:
  59. BindShaderProgram(state, arg0);
  60. break;
  61. case MacroHLEFunctionName.ClearColor:
  62. ClearColor(state, arg0);
  63. break;
  64. case MacroHLEFunctionName.ClearDepthStencil:
  65. ClearDepthStencil(state, arg0);
  66. break;
  67. case MacroHLEFunctionName.DrawArraysInstanced:
  68. DrawArraysInstanced(state, arg0);
  69. break;
  70. case MacroHLEFunctionName.DrawElements:
  71. DrawElements(state, arg0);
  72. break;
  73. case MacroHLEFunctionName.DrawElementsInstanced:
  74. DrawElementsInstanced(state, arg0);
  75. break;
  76. case MacroHLEFunctionName.DrawElementsIndirect:
  77. DrawElementsIndirect(state, arg0);
  78. break;
  79. case MacroHLEFunctionName.MultiDrawElementsIndirectCount:
  80. MultiDrawElementsIndirectCount(state, arg0);
  81. break;
  82. case MacroHLEFunctionName.UpdateBlendState:
  83. UpdateBlendState(state, arg0);
  84. break;
  85. case MacroHLEFunctionName.UpdateColorMasks:
  86. UpdateColorMasks(state, arg0);
  87. break;
  88. case MacroHLEFunctionName.UpdateUniformBufferState:
  89. UpdateUniformBufferState(state, arg0);
  90. break;
  91. case MacroHLEFunctionName.UpdateUniformBufferStateCbu:
  92. UpdateUniformBufferStateCbu(state, arg0);
  93. break;
  94. case MacroHLEFunctionName.UpdateUniformBufferStateCbuV2:
  95. UpdateUniformBufferStateCbuV2(state, arg0);
  96. break;
  97. default:
  98. throw new NotImplementedException(_functionName.ToString());
  99. }
  100. // It should be empty at this point, but clear it just to be safe.
  101. Fifo.Clear();
  102. }
  103. /// <summary>
  104. /// Binds a shader program with the index in arg0.
  105. /// </summary>
  106. /// <param name="state">GPU state at the time of the call</param>
  107. /// <param name="arg0">First argument of the call</param>
  108. private void BindShaderProgram(IDeviceState state, int arg0)
  109. {
  110. int scratchOffset = ShaderIdScratchOffset + arg0 * 4;
  111. int lastId = state.Read(scratchOffset);
  112. int id = FetchParam().Word;
  113. int offset = FetchParam().Word;
  114. if (lastId == id)
  115. {
  116. FetchParam();
  117. FetchParam();
  118. return;
  119. }
  120. _processor.ThreedClass.SetShaderOffset(arg0, (uint)offset);
  121. // Removes overflow on the method address into the increment portion.
  122. // Present in the original macro.
  123. int addrMask = unchecked((int)0xfffc0fff) << 2;
  124. state.Write(scratchOffset & addrMask, id);
  125. state.Write((ShaderAddressScratchOffset + arg0 * 4) & addrMask, offset);
  126. int stage = FetchParam().Word;
  127. uint cbAddress = (uint)FetchParam().Word;
  128. _processor.ThreedClass.UpdateUniformBufferState(65536, cbAddress >> 24, cbAddress << 8);
  129. int stageOffset = (stage & 0x7f) << 3;
  130. state.Write((UniformBufferBindVertexOffset + stageOffset * 4) & addrMask, 17);
  131. }
  132. /// <summary>
  133. /// Updates uniform buffer state for update or bind.
  134. /// </summary>
  135. /// <param name="state">GPU state at the time of the call</param>
  136. /// <param name="arg0">First argument of the call</param>
  137. private void UpdateUniformBufferState(IDeviceState state, int arg0)
  138. {
  139. uint address = (uint)state.Read(UpdateConstantBufferAddressesBase + arg0 * 4);
  140. int size = state.Read(UpdateConstantBufferSizesBase + arg0 * 4);
  141. _processor.ThreedClass.UpdateUniformBufferState(size, address >> 24, address << 8);
  142. }
  143. /// <summary>
  144. /// Updates uniform buffer state for update.
  145. /// </summary>
  146. /// <param name="state">GPU state at the time of the call</param>
  147. /// <param name="arg0">First argument of the call</param>
  148. private void UpdateUniformBufferStateCbu(IDeviceState state, int arg0)
  149. {
  150. uint address = (uint)state.Read(UpdateConstantBufferAddressCbu);
  151. UniformBufferState ubState = new()
  152. {
  153. Address = new()
  154. {
  155. High = address >> 24,
  156. Low = address << 8
  157. },
  158. Size = 24320,
  159. Offset = arg0 << 2
  160. };
  161. _processor.ThreedClass.UpdateUniformBufferState(ubState);
  162. }
  163. /// <summary>
  164. /// Updates uniform buffer state for update.
  165. /// </summary>
  166. /// <param name="state">GPU state at the time of the call</param>
  167. /// <param name="arg0">First argument of the call</param>
  168. private void UpdateUniformBufferStateCbuV2(IDeviceState state, int arg0)
  169. {
  170. uint address = (uint)state.Read(UpdateConstantBufferAddressCbu);
  171. UniformBufferState ubState = new()
  172. {
  173. Address = new()
  174. {
  175. High = address >> 24,
  176. Low = address << 8
  177. },
  178. Size = 28672,
  179. Offset = arg0 << 2
  180. };
  181. _processor.ThreedClass.UpdateUniformBufferState(ubState);
  182. }
  183. /// <summary>
  184. /// Updates blend enable using the given argument.
  185. /// </summary>
  186. /// <param name="state">GPU state at the time of the call</param>
  187. /// <param name="arg0">First argument of the call</param>
  188. private void UpdateBlendState(IDeviceState state, int arg0)
  189. {
  190. state.Write(LogicOpOffset, 0);
  191. Array8<Boolean32> enable = new();
  192. for (int i = 0; i < 8; i++)
  193. {
  194. enable[i] = new Boolean32((uint)(arg0 >> (i + 8)) & 1);
  195. }
  196. _processor.ThreedClass.UpdateBlendEnable(ref enable);
  197. }
  198. /// <summary>
  199. /// Updates color masks using the given argument and three pushed arguments.
  200. /// </summary>
  201. /// <param name="state">GPU state at the time of the call</param>
  202. /// <param name="arg0">First argument of the call</param>
  203. private void UpdateColorMasks(IDeviceState state, int arg0)
  204. {
  205. Array8<RtColorMask> masks = new();
  206. int index = 0;
  207. for (int i = 0; i < 4; i++)
  208. {
  209. masks[index++] = new RtColorMask((uint)arg0 & 0x1fff);
  210. masks[index++] = new RtColorMask(((uint)arg0 >> 16) & 0x1fff);
  211. if (i != 3)
  212. {
  213. arg0 = FetchParam().Word;
  214. }
  215. }
  216. _processor.ThreedClass.UpdateColorMasks(ref masks);
  217. }
  218. /// <summary>
  219. /// Clears one bound color target.
  220. /// </summary>
  221. /// <param name="state">GPU state at the time of the call</param>
  222. /// <param name="arg0">First argument of the call</param>
  223. private void ClearColor(IDeviceState state, int arg0)
  224. {
  225. int index = (arg0 >> 6) & 0xf;
  226. int layerCount = state.Read(ColorLayerCountOffset + index * ColorStructSize);
  227. _processor.ThreedClass.Clear(arg0, layerCount);
  228. }
  229. /// <summary>
  230. /// Clears the current depth-stencil target.
  231. /// </summary>
  232. /// <param name="state">GPU state at the time of the call</param>
  233. /// <param name="arg0">First argument of the call</param>
  234. private void ClearDepthStencil(IDeviceState state, int arg0)
  235. {
  236. int layerCount = state.Read(ZetaLayerCountOffset);
  237. _processor.ThreedClass.Clear(arg0, layerCount);
  238. }
  239. /// <summary>
  240. /// Performs a draw.
  241. /// </summary>
  242. /// <param name="state">GPU state at the time of the call</param>
  243. /// <param name="arg0">First argument of the call</param>
  244. private void DrawArraysInstanced(IDeviceState state, int arg0)
  245. {
  246. var topology = (PrimitiveTopology)arg0;
  247. var count = FetchParam();
  248. var instanceCount = FetchParam();
  249. var firstVertex = FetchParam();
  250. var firstInstance = FetchParam();
  251. if (ShouldSkipDraw(state, instanceCount.Word))
  252. {
  253. return;
  254. }
  255. _processor.ThreedClass.Draw(
  256. topology,
  257. count.Word,
  258. instanceCount.Word,
  259. 0,
  260. firstVertex.Word,
  261. firstInstance.Word,
  262. indexed: false);
  263. }
  264. /// <summary>
  265. /// Performs a indexed draw.
  266. /// </summary>
  267. /// <param name="state">GPU state at the time of the call</param>
  268. /// <param name="arg0">First argument of the call</param>
  269. private void DrawElements(IDeviceState state, int arg0)
  270. {
  271. var topology = (PrimitiveTopology)arg0;
  272. var indexAddressHigh = FetchParam();
  273. var indexAddressLow = FetchParam();
  274. var indexType = FetchParam();
  275. var firstIndex = 0;
  276. var indexCount = FetchParam();
  277. _processor.ThreedClass.UpdateIndexBuffer(
  278. (uint)indexAddressHigh.Word,
  279. (uint)indexAddressLow.Word,
  280. (IndexType)indexType.Word);
  281. _processor.ThreedClass.Draw(
  282. topology,
  283. indexCount.Word,
  284. 1,
  285. firstIndex,
  286. state.Read(FirstVertexOffset),
  287. 0,
  288. indexed: true);
  289. }
  290. /// <summary>
  291. /// Performs a indexed draw.
  292. /// </summary>
  293. /// <param name="state">GPU state at the time of the call</param>
  294. /// <param name="arg0">First argument of the call</param>
  295. private void DrawElementsInstanced(IDeviceState state, int arg0)
  296. {
  297. var topology = (PrimitiveTopology)arg0;
  298. var count = FetchParam();
  299. var instanceCount = FetchParam();
  300. var firstIndex = FetchParam();
  301. var firstVertex = FetchParam();
  302. var firstInstance = FetchParam();
  303. if (ShouldSkipDraw(state, instanceCount.Word))
  304. {
  305. return;
  306. }
  307. _processor.ThreedClass.Draw(
  308. topology,
  309. count.Word,
  310. instanceCount.Word,
  311. firstIndex.Word,
  312. firstVertex.Word,
  313. firstInstance.Word,
  314. indexed: true);
  315. }
  316. /// <summary>
  317. /// Performs a indirect indexed draw, with parameters from a GPU buffer.
  318. /// </summary>
  319. /// <param name="state">GPU state at the time of the call</param>
  320. /// <param name="arg0">First argument of the call</param>
  321. private void DrawElementsIndirect(IDeviceState state, int arg0)
  322. {
  323. var topology = (PrimitiveTopology)arg0;
  324. var count = FetchParam();
  325. var instanceCount = FetchParam();
  326. var firstIndex = FetchParam();
  327. var firstVertex = FetchParam();
  328. var firstInstance = FetchParam();
  329. ulong indirectBufferGpuVa = count.GpuVa;
  330. var bufferCache = _processor.MemoryManager.Physical.BufferCache;
  331. bool useBuffer = bufferCache.CheckModified(_processor.MemoryManager, indirectBufferGpuVa, IndirectIndexedDataEntrySize, out ulong indirectBufferAddress);
  332. if (useBuffer)
  333. {
  334. int indexCount = firstIndex.Word + count.Word;
  335. _processor.ThreedClass.DrawIndirect(
  336. topology,
  337. new MultiRange(indirectBufferAddress, IndirectIndexedDataEntrySize),
  338. default,
  339. 1,
  340. IndirectIndexedDataEntrySize,
  341. indexCount,
  342. IndirectDrawType.DrawIndexedIndirect);
  343. }
  344. else
  345. {
  346. if (ShouldSkipDraw(state, instanceCount.Word))
  347. {
  348. return;
  349. }
  350. _processor.ThreedClass.Draw(
  351. topology,
  352. count.Word,
  353. instanceCount.Word,
  354. firstIndex.Word,
  355. firstVertex.Word,
  356. firstInstance.Word,
  357. indexed: true);
  358. }
  359. }
  360. /// <summary>
  361. /// Performs a indirect indexed multi-draw, with parameters from a GPU buffer.
  362. /// </summary>
  363. /// <param name="state">GPU state at the time of the call</param>
  364. /// <param name="arg0">First argument of the call</param>
  365. private void MultiDrawElementsIndirectCount(IDeviceState state, int arg0)
  366. {
  367. int arg1 = FetchParam().Word;
  368. int arg2 = FetchParam().Word;
  369. int arg3 = FetchParam().Word;
  370. int startDraw = arg0;
  371. int endDraw = arg1;
  372. var topology = (PrimitiveTopology)arg2;
  373. int paddingWords = arg3;
  374. int stride = paddingWords * 4 + 0x14;
  375. ulong parameterBufferGpuVa = FetchParam().GpuVa;
  376. int maxDrawCount = endDraw - startDraw;
  377. if (startDraw != 0)
  378. {
  379. int drawCount = _processor.MemoryManager.Read<int>(parameterBufferGpuVa, tracked: true);
  380. // Calculate maximum draw count based on the previous draw count and current draw count.
  381. if ((uint)drawCount <= (uint)startDraw)
  382. {
  383. // The start draw is past our total draw count, so all draws were already performed.
  384. maxDrawCount = 0;
  385. }
  386. else
  387. {
  388. // Perform just the missing number of draws.
  389. maxDrawCount = (int)Math.Min((uint)maxDrawCount, (uint)(drawCount - startDraw));
  390. }
  391. }
  392. if (maxDrawCount == 0)
  393. {
  394. Fifo.Clear();
  395. return;
  396. }
  397. ulong indirectBufferGpuVa = 0;
  398. int indexCount = 0;
  399. for (int i = 0; i < maxDrawCount; i++)
  400. {
  401. var count = FetchParam();
  402. #pragma warning disable IDE0059 // Remove unnecessary value assignment
  403. var instanceCount = FetchParam();
  404. var firstIndex = FetchParam();
  405. var firstVertex = FetchParam();
  406. var firstInstance = FetchParam();
  407. #pragma warning restore IDE0059
  408. if (i == 0)
  409. {
  410. indirectBufferGpuVa = count.GpuVa;
  411. }
  412. indexCount = Math.Max(indexCount, count.Word + firstIndex.Word);
  413. if (i != maxDrawCount - 1)
  414. {
  415. for (int j = 0; j < paddingWords; j++)
  416. {
  417. FetchParam();
  418. }
  419. }
  420. }
  421. var bufferCache = _processor.MemoryManager.Physical.BufferCache;
  422. ulong indirectBufferSize = (ulong)maxDrawCount * (ulong)stride;
  423. MultiRange indirectBufferRange = bufferCache.TranslateAndCreateMultiBuffers(_processor.MemoryManager, indirectBufferGpuVa, indirectBufferSize, BufferStage.Indirect);
  424. MultiRange parameterBufferRange = bufferCache.TranslateAndCreateMultiBuffers(_processor.MemoryManager, parameterBufferGpuVa, 4, BufferStage.Indirect);
  425. _processor.ThreedClass.DrawIndirect(
  426. topology,
  427. indirectBufferRange,
  428. parameterBufferRange,
  429. maxDrawCount,
  430. stride,
  431. indexCount,
  432. Threed.IndirectDrawType.DrawIndexedIndirectCount);
  433. }
  434. /// <summary>
  435. /// Checks if the draw should be skipped, because the masked instance count is zero.
  436. /// </summary>
  437. /// <param name="state">Current GPU state</param>
  438. /// <param name="instanceCount">Draw instance count</param>
  439. /// <returns>True if the draw should be skipped, false otherwise</returns>
  440. private static bool ShouldSkipDraw(IDeviceState state, int instanceCount)
  441. {
  442. return (Read(state, 0xd1b) & instanceCount) == 0;
  443. }
  444. /// <summary>
  445. /// Fetches a arguments from the arguments FIFO.
  446. /// </summary>
  447. /// <returns>The call argument, or a 0 value with null address if the FIFO is empty</returns>
  448. private FifoWord FetchParam()
  449. {
  450. if (!Fifo.TryDequeue(out var value))
  451. {
  452. Logger.Warning?.Print(LogClass.Gpu, "Macro attempted to fetch an inexistent argument.");
  453. return new FifoWord(0UL, 0);
  454. }
  455. return value;
  456. }
  457. /// <summary>
  458. /// Reads data from a GPU register.
  459. /// </summary>
  460. /// <param name="state">Current GPU state</param>
  461. /// <param name="reg">Register offset to read</param>
  462. /// <returns>GPU register value</returns>
  463. private static int Read(IDeviceState state, int reg)
  464. {
  465. return state.Read(reg * 4);
  466. }
  467. }
  468. }