aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Graphics.Gpu/Synchronization/SynchronizationManager.cs
blob: ccec763e36ed12832b46045aad2d16b684ee2f6c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
using Ryujinx.Common.Logging;
using System;
using System.Threading;

namespace Ryujinx.Graphics.Gpu.Synchronization
{
    /// <summary>
    /// GPU synchronization manager.
    /// </summary>
    public class SynchronizationManager
    {
        /// <summary>
        /// The maximum number of syncpoints supported by the GM20B.
        /// </summary>
        public const int MaxHardwareSyncpoints = 192;

        /// <summary>
        /// Array containing all hardware syncpoints.
        /// </summary>
        private readonly Syncpoint[] _syncpoints;

        public SynchronizationManager()
        {
            _syncpoints = new Syncpoint[MaxHardwareSyncpoints];

            for (uint i = 0; i < _syncpoints.Length; i++)
            {
                _syncpoints[i] = new Syncpoint(i);
            }
        }

        /// <summary>
        /// Increment the value of a syncpoint with a given id.
        /// </summary>
        /// <param name="id">The id of the syncpoint</param>
        /// <exception cref="System.ArgumentOutOfRangeException">Thrown when id >= MaxHardwareSyncpoints</exception>
        /// <returns>The incremented value of the syncpoint</returns>
        public uint IncrementSyncpoint(uint id)
        {
            if (id >= MaxHardwareSyncpoints)
            {
                throw new ArgumentOutOfRangeException(nameof(id));
            }

            return _syncpoints[id].Increment();
        }

        /// <summary>
        /// Get the value of a syncpoint with a given id.
        /// </summary>
        /// <param name="id">The id of the syncpoint</param>
        /// <exception cref="System.ArgumentOutOfRangeException">Thrown when id >= MaxHardwareSyncpoints</exception>
        /// <returns>The value of the syncpoint</returns>
        public uint GetSyncpointValue(uint id)
        {
            if (id >= MaxHardwareSyncpoints)
            {
                throw new ArgumentOutOfRangeException(nameof(id));
            }

            return _syncpoints[id].Value;
        }

        /// <summary>
        /// Register a new callback on a syncpoint with a given id at a target threshold.
        /// The callback will be called once the threshold is reached and will automatically be unregistered.
        /// </summary>
        /// <param name="id">The id of the syncpoint</param>
        /// <param name="threshold">The target threshold</param>
        /// <param name="callback">The callback to call when the threshold is reached</param>
        /// <exception cref="System.ArgumentOutOfRangeException">Thrown when id >= MaxHardwareSyncpoints</exception>
        /// <returns>The created SyncpointWaiterHandle object or null if already past threshold</returns>
        public SyncpointWaiterHandle RegisterCallbackOnSyncpoint(uint id, uint threshold, Action<SyncpointWaiterHandle> callback)
        {
            if (id >= MaxHardwareSyncpoints)
            {
                throw new ArgumentOutOfRangeException(nameof(id));
            }

            return _syncpoints[id].RegisterCallback(threshold, callback);
        }

        /// <summary>
        /// Unregister a callback on a given syncpoint.
        /// </summary>
        /// <param name="id">The id of the syncpoint</param>
        /// <param name="waiterInformation">The waiter information to unregister</param>
        /// <exception cref="System.ArgumentOutOfRangeException">Thrown when id >= MaxHardwareSyncpoints</exception>
        public void UnregisterCallback(uint id, SyncpointWaiterHandle waiterInformation)
        {
            if (id >= MaxHardwareSyncpoints)
            {
                throw new ArgumentOutOfRangeException(nameof(id));
            }

            _syncpoints[id].UnregisterCallback(waiterInformation);
        }

        /// <summary>
        /// Wait on a syncpoint with a given id at a target threshold.
        /// The callback will be called once the threshold is reached and will automatically be unregistered.
        /// </summary>
        /// <param name="id">The id of the syncpoint</param>
        /// <param name="threshold">The target threshold</param>
        /// <param name="timeout">The timeout</param>
        /// <exception cref="System.ArgumentOutOfRangeException">Thrown when id >= MaxHardwareSyncpoints</exception>
        /// <returns>True if timed out</returns>
        public bool WaitOnSyncpoint(uint id, uint threshold, TimeSpan timeout)
        {
            if (id >= MaxHardwareSyncpoints)
            {
                throw new ArgumentOutOfRangeException(nameof(id));
            }

            // TODO: Remove this when GPU channel scheduling will be implemented.
            if (timeout == Timeout.InfiniteTimeSpan)
            {
                timeout = TimeSpan.FromSeconds(1);
            }

            using ManualResetEvent waitEvent = new(false);
            var info = _syncpoints[id].RegisterCallback(threshold, (x) => waitEvent.Set());

            if (info == null)
            {
                return false;
            }

            bool signaled = waitEvent.WaitOne(timeout);

            if (!signaled && info != null)
            {
                Logger.Error?.Print(LogClass.Gpu, $"Wait on syncpoint {id} for threshold {threshold} took more than {timeout.TotalMilliseconds}ms, resuming execution...");

                _syncpoints[id].UnregisterCallback(info);
            }

            return !signaled;
        }
    }
}