aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Profiler/UI/ProfileWindowGraph.cs
blob: 6a4a52a998004f6f728fadc64365913059f61ef4 (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
using System;
using OpenTK;
using OpenTK.Graphics.OpenGL;
using Ryujinx.Common;

namespace Ryujinx.Profiler.UI
{
    public partial class ProfileWindow
    {
        // Color index equal to timing flag type as int
        private Color[] _timingFlagColors = new[]
        {
            new Color(150, 25, 25, 50), // FrameSwap   = 0
            new Color(25, 25, 150, 50), // SystemFrame = 1
        };

        private TimingFlag[] _timingFlags;

        private const float GraphMoveSpeed = 40000;
        private const float GraphZoomSpeed = 50;

        private float _graphZoom      = 1;
        private float _graphPosition  = 0;

        private void DrawGraph(float xOffset, float yOffset, float width)
        {
            if (_sortedProfileData.Count != 0)
            {
                int   left, right;
                float top, bottom;

                int    verticalIndex      = 0;
                float  graphRight         = xOffset + width;
                float  barHeight          = (LineHeight - LinePadding);
                long   history            = Profile.HistoryLength;
                double timeWidthTicks     = history / (double)_graphZoom;
                long   graphPositionTicks = (long)(_graphPosition * PerformanceCounter.TicksPerMillisecond);
                long   ticksPerPixel      = (long)(timeWidthTicks / width);

                // Reset start point if out of bounds
                if (timeWidthTicks + graphPositionTicks > history)
                {
                    graphPositionTicks = history - (long)timeWidthTicks;
                    _graphPosition     = (float)graphPositionTicks / PerformanceCounter.TicksPerMillisecond;
                }

                graphPositionTicks = _captureTime - graphPositionTicks;

                GL.Enable(EnableCap.ScissorTest);

                // Draw timing flags
                if (_displayFlags)
                {
                    TimingFlagType prevType = TimingFlagType.Count;

                    GL.Enable(EnableCap.Blend);
                    GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);

                    GL.Begin(PrimitiveType.Lines);
                    foreach (TimingFlag timingFlag in _timingFlags)
                    {
                        if (prevType != timingFlag.FlagType)
                        {
                            prevType = timingFlag.FlagType;
                            GL.Color4(_timingFlagColors[(int)prevType]);
                        }

                        int x = (int)(graphRight - ((graphPositionTicks - timingFlag.Timestamp) / timeWidthTicks) * width);
                        GL.Vertex2(x, 0);
                        GL.Vertex2(x, Height);
                    }
                    GL.End();
                    GL.Disable(EnableCap.Blend);
                }

                // Draw bars
                GL.Begin(PrimitiveType.Triangles);
                foreach (var entry in _sortedProfileData)
                {
                    long furthest = 0;

                    bottom = GetLineY(yOffset, LineHeight, LinePadding, true, verticalIndex);
                    top    = bottom + barHeight;

                    // Skip rendering out of bounds bars
                    if (top < 0 || bottom > Height)
                    {
                        verticalIndex++;
                        continue;
                    }


                    GL.Color3(Color.Green);
                    foreach (Timestamp timestamp in entry.Value.GetAllTimestamps())
                    {
                        // Skip drawing multiple timestamps on same pixel
                        if (timestamp.EndTime < furthest)
                            continue;
                        furthest = timestamp.EndTime + ticksPerPixel;

                        left  = (int)(graphRight - ((graphPositionTicks - timestamp.BeginTime) / timeWidthTicks) * width);
                        right = (int)(graphRight - ((graphPositionTicks - timestamp.EndTime)   / timeWidthTicks) * width);

                        // Make sure width is at least 1px
                        right = Math.Max(left + 1, right);

                        GL.Vertex2(left,  bottom);
                        GL.Vertex2(left,  top);
                        GL.Vertex2(right, top);

                        GL.Vertex2(right, top);
                        GL.Vertex2(right, bottom);
                        GL.Vertex2(left,  bottom);
                    }

                    // Currently capturing timestamp
                    GL.Color3(Color.Red);
                    long entryBegin = entry.Value.BeginTime;
                    if (entryBegin != -1)
                    {
                        left = (int)(graphRight - ((graphPositionTicks - entryBegin) / timeWidthTicks) * width);

                        // Make sure width is at least 1px
                        left = Math.Min(left - 1, (int)graphRight);

                        GL.Vertex2(left,       bottom);
                        GL.Vertex2(left,       top);
                        GL.Vertex2(graphRight, top);

                        GL.Vertex2(graphRight, top);
                        GL.Vertex2(graphRight, bottom);
                        GL.Vertex2(left,       bottom);
                    }

                    verticalIndex++;
                }

                GL.End();
                GL.Disable(EnableCap.ScissorTest);

                string label = $"-{MathF.Round(_graphPosition, 2)} ms";

                // Dummy draw for measure
                float labelWidth = _fontService.DrawText(label, 0, 0, LineHeight, false);
                _fontService.DrawText(label, graphRight - labelWidth - LinePadding, FilterHeight + LinePadding, LineHeight);
                
                _fontService.DrawText($"-{MathF.Round((float)((timeWidthTicks / PerformanceCounter.TicksPerMillisecond) + _graphPosition), 2)} ms", xOffset + LinePadding, FilterHeight + LinePadding, LineHeight);
            }
        }
    }
}