using Ryujinx.Graphics.GAL; using System; namespace Ryujinx.Graphics.Gpu.Engine.Threed { /// /// Semaphore updater. /// class SemaphoreUpdater { /// /// GPU semaphore operation. /// private enum SemaphoreOperation { Release = 0, Acquire = 1, Counter = 2 } /// /// Counter type for GPU counter reset. /// private enum ResetCounterType { SamplesPassed = 1, ZcullStats = 2, TransformFeedbackPrimitivesWritten = 0x10, InputVertices = 0x12, InputPrimitives = 0x13, VertexShaderInvocations = 0x15, TessControlShaderInvocations = 0x16, TessEvaluationShaderInvocations = 0x17, TessEvaluationShaderPrimitives = 0x18, GeometryShaderInvocations = 0x1a, GeometryShaderPrimitives = 0x1b, ClipperInputPrimitives = 0x1c, ClipperOutputPrimitives = 0x1d, FragmentShaderInvocations = 0x1e, PrimitivesGenerated = 0x1f } /// /// Counter type for GPU counter reporting. /// private enum ReportCounterType { Payload = 0, InputVertices = 1, InputPrimitives = 3, VertexShaderInvocations = 5, GeometryShaderInvocations = 7, GeometryShaderPrimitives = 9, ZcullStats0 = 0xa, TransformFeedbackPrimitivesWritten = 0xb, ZcullStats1 = 0xc, ZcullStats2 = 0xe, ClipperInputPrimitives = 0xf, ZcullStats3 = 0x10, ClipperOutputPrimitives = 0x11, PrimitivesGenerated = 0x12, FragmentShaderInvocations = 0x13, SamplesPassed = 0x15, TransformFeedbackOffset = 0x1a, TessControlShaderInvocations = 0x1b, TessEvaluationShaderInvocations = 0x1d, TessEvaluationShaderPrimitives = 0x1f } private readonly GpuContext _context; private readonly GpuChannel _channel; private readonly DeviceStateWithShadow _state; /// /// Creates a new instance of the semaphore updater. /// /// GPU context /// GPU channel /// Channel state public SemaphoreUpdater(GpuContext context, GpuChannel channel, DeviceStateWithShadow state) { _context = context; _channel = channel; _state = state; } /// /// Resets the value of an internal GPU counter back to zero. /// /// Method call argument public void ResetCounter(int argument) { ResetCounterType type = (ResetCounterType)argument; switch (type) { case ResetCounterType.SamplesPassed: _context.Renderer.ResetCounter(CounterType.SamplesPassed); break; case ResetCounterType.PrimitivesGenerated: _context.Renderer.ResetCounter(CounterType.PrimitivesGenerated); break; case ResetCounterType.TransformFeedbackPrimitivesWritten: _context.Renderer.ResetCounter(CounterType.TransformFeedbackPrimitivesWritten); break; } } /// /// Writes a GPU counter to guest memory. /// /// Method call argument public void Report(int argument) { SemaphoreOperation op = (SemaphoreOperation)(argument & 3); ReportCounterType type = (ReportCounterType)((argument >> 23) & 0x1f); switch (op) { case SemaphoreOperation.Release: ReleaseSemaphore(); break; case SemaphoreOperation.Counter: ReportCounter(type); break; } } /// /// Writes (or Releases) a GPU semaphore value to guest memory. /// private void ReleaseSemaphore() { _channel.MemoryManager.Write(_state.State.SemaphoreAddress.Pack(), _state.State.SemaphorePayload); _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. /// /// Counter to be written to memory private void ReportCounter(ReportCounterType type) { ulong gpuVa = _state.State.SemaphoreAddress.Pack(); ulong ticks = _context.GetTimestamp(); ICounterEvent counter = null; void resultHandler(object evt, ulong result) { CounterData counterData = new CounterData { Counter = result, Timestamp = ticks }; if (counter?.Invalid != true) { _channel.MemoryManager.Write(gpuVa, counterData); } } switch (type) { case ReportCounterType.Payload: resultHandler(null, (ulong)_state.State.SemaphorePayload); break; case ReportCounterType.SamplesPassed: counter = _context.Renderer.ReportCounter(CounterType.SamplesPassed, resultHandler, false); break; case ReportCounterType.PrimitivesGenerated: counter = _context.Renderer.ReportCounter(CounterType.PrimitivesGenerated, resultHandler, false); break; case ReportCounterType.TransformFeedbackPrimitivesWritten: counter = _context.Renderer.ReportCounter(CounterType.TransformFeedbackPrimitivesWritten, resultHandler, false); break; } _channel.MemoryManager.CounterCache.AddOrUpdate(gpuVa, counter); } } }