MethodReport.cs 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. using Ryujinx.Common;
  2. using Ryujinx.Graphics.GAL;
  3. using Ryujinx.Graphics.Gpu.Memory;
  4. using Ryujinx.Graphics.Gpu.State;
  5. using System;
  6. using System.Runtime.InteropServices;
  7. namespace Ryujinx.Graphics.Gpu.Engine
  8. {
  9. partial class Methods
  10. {
  11. private const int NsToTicksFractionNumerator = 384;
  12. private const int NsToTicksFractionDenominator = 625;
  13. private ulong _runningCounter;
  14. private readonly CounterCache _counterCache = new CounterCache();
  15. /// <summary>
  16. /// Writes a GPU counter to guest memory.
  17. /// </summary>
  18. /// <param name="state">Current GPU state</param>
  19. /// <param name="argument">Method call argument</param>
  20. private void Report(GpuState state, int argument)
  21. {
  22. ReportMode mode = (ReportMode)(argument & 3);
  23. ReportCounterType type = (ReportCounterType)((argument >> 23) & 0x1f);
  24. switch (mode)
  25. {
  26. case ReportMode.Release: ReleaseSemaphore(state); break;
  27. case ReportMode.Counter: ReportCounter(state, type); break;
  28. }
  29. }
  30. /// <summary>
  31. /// Writes (or Releases) a GPU semaphore value to guest memory.
  32. /// </summary>
  33. /// <param name="state">Current GPU state</param>
  34. private void ReleaseSemaphore(GpuState state)
  35. {
  36. var rs = state.Get<ReportState>(MethodOffset.ReportState);
  37. _context.MemoryAccessor.Write(rs.Address.Pack(), rs.Payload);
  38. _context.AdvanceSequence();
  39. }
  40. /// <summary>
  41. /// Packed GPU counter data (including GPU timestamp) in memory.
  42. /// </summary>
  43. private struct CounterData
  44. {
  45. public ulong Counter;
  46. public ulong Timestamp;
  47. }
  48. /// <summary>
  49. /// Writes a GPU counter to guest memory.
  50. /// This also writes the current timestamp value.
  51. /// </summary>
  52. /// <param name="state">Current GPU state</param>
  53. /// <param name="type">Counter to be written to memory</param>
  54. private void ReportCounter(GpuState state, ReportCounterType type)
  55. {
  56. CounterData counterData = new CounterData();
  57. ulong counter = 0;
  58. switch (type)
  59. {
  60. case ReportCounterType.Zero:
  61. counter = 0;
  62. break;
  63. case ReportCounterType.SamplesPassed:
  64. counter = _context.Renderer.GetCounter(CounterType.SamplesPassed);
  65. break;
  66. case ReportCounterType.PrimitivesGenerated:
  67. counter = _context.Renderer.GetCounter(CounterType.PrimitivesGenerated);
  68. break;
  69. case ReportCounterType.TransformFeedbackPrimitivesWritten:
  70. counter = _context.Renderer.GetCounter(CounterType.TransformFeedbackPrimitivesWritten);
  71. break;
  72. }
  73. ulong ticks = ConvertNanosecondsToTicks((ulong)PerformanceCounter.ElapsedNanoseconds);
  74. if (GraphicsConfig.FastGpuTime)
  75. {
  76. // Divide by some amount to report time as if operations were performed faster than they really are.
  77. // This can prevent some games from switching to a lower resolution because rendering is too slow.
  78. ticks /= 256;
  79. }
  80. counterData.Counter = counter;
  81. counterData.Timestamp = ticks;
  82. Span<CounterData> counterDataSpan = MemoryMarshal.CreateSpan(ref counterData, 1);
  83. Span<byte> data = MemoryMarshal.Cast<CounterData, byte>(counterDataSpan);
  84. var rs = state.Get<ReportState>(MethodOffset.ReportState);
  85. _context.MemoryAccessor.Write(rs.Address.Pack(), data);
  86. _counterCache.AddOrUpdate(rs.Address.Pack());
  87. }
  88. /// <summary>
  89. /// Converts a nanoseconds timestamp value to Maxwell time ticks.
  90. /// </summary>
  91. /// <remarks>
  92. /// The frequency is 614400000 Hz.
  93. /// </remarks>
  94. /// <param name="nanoseconds">Timestamp in nanoseconds</param>
  95. /// <returns>Maxwell ticks</returns>
  96. private static ulong ConvertNanosecondsToTicks(ulong nanoseconds)
  97. {
  98. // We need to divide first to avoid overflows.
  99. // We fix up the result later by calculating the difference and adding
  100. // that to the result.
  101. ulong divided = nanoseconds / NsToTicksFractionDenominator;
  102. ulong rounded = divided * NsToTicksFractionDenominator;
  103. ulong errorBias = (nanoseconds - rounded) * NsToTicksFractionNumerator / NsToTicksFractionDenominator;
  104. return divided * NsToTicksFractionNumerator + errorBias;
  105. }
  106. }
  107. }