MacroHLE.cs 5.9 KB

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