aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Profiler/TimingInfo.cs
blob: 6058ddbd81907f53230330590fe450625001bc9b (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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
using System;
using System.Collections.Generic;

namespace Ryujinx.Profiler
{
    public struct Timestamp
    {
        public long BeginTime;
        public long EndTime;
    }

    public class TimingInfo
    {
        // Timestamps
        public long TotalTime { get; set; }
        public long Instant   { get; set; }

        // Measurement counts
        public int  Count        { get; set; }
        public int  InstantCount { get; set; }

        // Work out average
        public long AverageTime => (Count == 0) ? -1 : TotalTime / Count;

        // Intentionally not locked as it's only a get count
        public bool IsActive => _timestamps.Count > 0;

        public long BeginTime
        {
            get
            {
                lock (_timestampLock)
                {
                    if (_depth > 0)
                    {
                        return _currentTimestamp.BeginTime;
                    }

                    return -1;
                }
            }
        }

        // Timestamp collection
        private List<Timestamp> _timestamps;
        private readonly object _timestampLock     = new object();
        private readonly object _timestampListLock = new object();
        private Timestamp _currentTimestamp;

        // Depth of current timer,
        // each begin call increments and each end call decrements
        private int _depth;

        public TimingInfo()
        {
            _timestamps = new List<Timestamp>();
            _depth      = 0;
        }

        public void Begin(long beginTime)
        {
            lock (_timestampLock)
            {
                // Finish current timestamp if already running
                if (_depth > 0)
                {
                    EndUnsafe(beginTime);
                }

                BeginUnsafe(beginTime);
                _depth++;
            }
        }

        private void BeginUnsafe(long beginTime)
        {
            _currentTimestamp.BeginTime = beginTime;
            _currentTimestamp.EndTime   = -1;
        }

        public void End(long endTime)
        {
            lock (_timestampLock)
            {
                _depth--;

                if (_depth < 0)
                {
                    throw new Exception("Timing info end called without corresponding begin");
                }

                EndUnsafe(endTime);

                // Still have others using this timing info so recreate start for them
                if (_depth > 0)
                {
                    BeginUnsafe(endTime);
                }
            }
        }

        private void EndUnsafe(long endTime)
        {
            _currentTimestamp.EndTime = endTime;
            lock (_timestampListLock)
            {
                _timestamps.Add(_currentTimestamp);
            }

            long delta = _currentTimestamp.EndTime - _currentTimestamp.BeginTime;
            TotalTime += delta;
            Instant   += delta;

            Count++;
            InstantCount++;
        }

        // Remove any timestamps before given timestamp to free memory
        public void Cleanup(long before, long preserveStart, long preserveEnd)
        {
            lock (_timestampListLock)
            {
                int toRemove        = 0;
                int toPreserveStart = 0;
                int toPreserveLen   = 0;

                for (int i = 0; i < _timestamps.Count; i++)
                {
                    if (_timestamps[i].EndTime < preserveStart)
                    {
                        toPreserveStart++;
                        InstantCount--;
                        Instant -= _timestamps[i].EndTime - _timestamps[i].BeginTime;
                    }
                    else if (_timestamps[i].EndTime < preserveEnd)
                    {
                        toPreserveLen++;
                    }
                    else if (_timestamps[i].EndTime < before)
                    {
                        toRemove++;
                        InstantCount--;
                        Instant -= _timestamps[i].EndTime - _timestamps[i].BeginTime;
                    }
                    else
                    {
                        // Assume timestamps are in chronological order so no more need to be removed
                        break;
                    }
                }

                if (toPreserveStart > 0)
                {
                    _timestamps.RemoveRange(0, toPreserveStart);
                }

                if (toRemove > 0)
                {
                    _timestamps.RemoveRange(toPreserveLen, toRemove);
                }
            }
        }

        public Timestamp[] GetAllTimestamps()
        {
            lock (_timestampListLock)
            {
                Timestamp[] returnTimestamps = new Timestamp[_timestamps.Count];
                _timestamps.CopyTo(returnTimestamps);
                return returnTimestamps;
            }
        }
    }
}