using Ryujinx.Common; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu.State; using System; using System.Runtime.InteropServices; namespace Ryujinx.Graphics.Gpu.Engine { partial class Methods { private const int NsToTicksFractionNumerator = 384; private const int NsToTicksFractionDenominator = 625; private ulong _runningCounter; /// /// Writes a GPU counter to guest memory. /// /// Current GPU state /// Method call argument private void Report(GpuState state, int argument) { ReportMode mode = (ReportMode)(argument & 3); ReportCounterType type = (ReportCounterType)((argument >> 23) & 0x1f); switch (mode) { case ReportMode.Semaphore: ReportSemaphore(state); break; case ReportMode.Counter: ReportCounter(state, type); break; } } /// /// Writes a GPU semaphore value to guest memory. /// /// Current GPU state private void ReportSemaphore(GpuState state) { var rs = state.Get(MethodOffset.ReportState); _context.MemoryAccessor.Write(rs.Address.Pack(), rs.Payload); _context.AdvanceSequence(); } /// /// Packed GPU counter data (including GPU timestamp) in memory. /// private struct CounterData { public ulong Counter; public ulong Timestamp; } /// /// Writes a GPU counter to guest memory. /// This also writes the current timestamp value. /// /// Current GPU state /// Counter to be written to memory private void ReportCounter(GpuState state, ReportCounterType type) { CounterData counterData = new CounterData(); ulong counter = 0; switch (type) { case ReportCounterType.Zero: counter = 0; break; case ReportCounterType.SamplesPassed: counter = _context.Renderer.GetCounter(CounterType.SamplesPassed); break; case ReportCounterType.PrimitivesGenerated: counter = _context.Renderer.GetCounter(CounterType.PrimitivesGenerated); break; case ReportCounterType.TransformFeedbackPrimitivesWritten: counter = _context.Renderer.GetCounter(CounterType.TransformFeedbackPrimitivesWritten); break; } ulong ticks; if (GraphicsConfig.FastGpuTime) { ticks = _runningCounter++; } else { ticks = ConvertNanosecondsToTicks((ulong)PerformanceCounter.ElapsedNanoseconds); } counterData.Counter = counter; counterData.Timestamp = ticks; Span counterDataSpan = MemoryMarshal.CreateSpan(ref counterData, 1); Span data = MemoryMarshal.Cast(counterDataSpan); var rs = state.Get(MethodOffset.ReportState); _context.MemoryAccessor.Write(rs.Address.Pack(), data); } /// /// Converts a nanoseconds timestamp value to Maxwell time ticks. /// /// /// The frequency is 614400000 Hz. /// /// Timestamp in nanoseconds /// Maxwell ticks private static ulong ConvertNanosecondsToTicks(ulong nanoseconds) { // We need to divide first to avoid overflows. // We fix up the result later by calculating the difference and adding // that to the result. ulong divided = nanoseconds / NsToTicksFractionDenominator; ulong rounded = divided * NsToTicksFractionDenominator; ulong errorBias = (nanoseconds - rounded) * NsToTicksFractionNumerator / NsToTicksFractionDenominator; return divided * NsToTicksFractionNumerator + errorBias; } } }