Line data Source code
1 : /**
2 : * @file rendertimers.cpp
3 : * @brief renderer functionality used for displaying rendering stats while the program is running
4 : *
5 : * timers can be created with designated start/stop points in the code; sub-ms
6 : * times needed for accurate diagnosis possible (each frame is ~16.6ms @ 60Hz)
7 : *
8 : * used in rendergl.cpp
9 : */
10 : #include "../libprimis-headers/cube.h"
11 : #include "../../shared/geomexts.h"
12 : #include "../../shared/glexts.h"
13 :
14 : #include "rendergl.h"
15 : #include "rendertext.h"
16 : #include "renderttf.h"
17 : #include "renderva.h"
18 : #include "renderwindow.h"
19 :
20 : #include "interface/control.h"
21 :
22 : void cleanuptimers(); //needed for timer script gvar
23 : VAR(frametimer, 0, 0, 1); //toggles timing how long each frame takes (and rendering it to timer ui), used in hud
24 :
25 : //declared and used as pointer in header
26 : struct timer final
27 : {
28 : enum
29 : {
30 : Timer_MaxQuery = 4 //max number of gl queries
31 : };
32 : const char *name; //name the timer reports as
33 : bool gpu; //whether the timer is for gpu time (true) or cpu time
34 : std::array<GLuint, Timer_MaxQuery> query; //gpu query information
35 : int waiting; //internal bitmask for queries
36 : size_t starttime; //time the timer was started (in terms of ms since game started)
37 : float result, //raw value of the timer, -1 if no info available
38 : print; //the time the timer displays: ms per frame for whatever object
39 : };
40 :
41 : //locally relevant functionality
42 : namespace
43 : {
44 : std::vector<timer> timers;
45 : std::vector<size_t> timerorder;
46 : size_t timercycle = 0;
47 :
48 0 : VARFN(timer, usetimers, 0, 0, 1, cleanuptimers()); //toggles logging timer information & rendering it
49 :
50 0 : timer *findtimer(const char *name, bool gpu) //also creates a new timer if none found
51 : {
52 0 : for(size_t i = 0; i < timers.size(); i++)
53 : {
54 0 : if(!std::strcmp(timers[i].name, name) && timers[i].gpu == gpu)
55 : {
56 0 : timerorder.erase(std::find(timerorder.begin(), timerorder.end(), i));
57 0 : timerorder.push_back(i);
58 0 : return &timers[i];
59 : }
60 : }
61 0 : timerorder.push_back(timers.size());
62 0 : timers.emplace_back();
63 0 : timer &t = timers.back();
64 0 : t.name = name;
65 0 : t.gpu = gpu;
66 0 : t.query.fill(0);
67 0 : if(gpu)
68 : {
69 0 : glGenQueries(timer::Timer_MaxQuery, t.query.data());
70 : }
71 0 : t.waiting = 0;
72 0 : t.starttime = 0;
73 0 : t.result = -1;
74 0 : t.print = -1;
75 0 : return &t;
76 : }
77 : }
78 :
79 : //externally relevant functionality
80 :
81 : //used to start a timer in some part of the code, cannot be used outside of rendering part
82 :
83 0 : timer *begintimer(const char *name, bool gpu)
84 : {
85 0 : if(!usetimers || inbetweenframes || (gpu && (!hasTQ || deferquery)))
86 : {
87 0 : return nullptr;
88 : }
89 0 : timer *t = findtimer(name, gpu);
90 0 : if(t->gpu)
91 : {
92 0 : deferquery++;
93 0 : glBeginQuery(GL_TIME_ELAPSED_EXT, t->query[timercycle]);
94 0 : t->waiting |= 1<<timercycle;
95 : }
96 : else
97 : {
98 0 : t->starttime = getclockmillis();
99 : }
100 0 : return t;
101 : }
102 :
103 : //used to end a timer started by begintimer(), needs to be included sometime after begintimer
104 : //the part between begintimer() and endtimer() is what gets timed
105 0 : void endtimer(timer *t)
106 : {
107 0 : if(!t)
108 : {
109 0 : return;
110 : }
111 0 : if(t->gpu)
112 : {
113 0 : glEndQuery(GL_TIME_ELAPSED_EXT);
114 0 : deferquery--;
115 : }
116 : else
117 : {
118 0 : t->result = std::max(static_cast<float>(getclockmillis() - t->starttime), 0.0f);
119 : }
120 : }
121 :
122 : //foreach timer, query what time has passed since last update
123 0 : void synctimers()
124 : {
125 0 : timercycle = (timercycle + 1) % timer::Timer_MaxQuery;
126 :
127 0 : for(timer& t : timers)
128 : {
129 0 : if(t.waiting&(1<<timercycle))
130 : {
131 0 : GLint available = 0;
132 0 : while(!available)
133 : {
134 0 : glGetQueryObjectiv(t.query[timercycle], GL_QUERY_RESULT_AVAILABLE, &available);
135 : }
136 0 : GLuint64EXT result = 0;
137 0 : glGetQueryObjectui64v(t.query[timercycle], GL_QUERY_RESULT, &result);
138 0 : t.result = std::max(static_cast<float>(result) * 1e-6f, 0.0f);
139 0 : t.waiting &= ~(1<<timercycle);
140 : }
141 : else
142 : {
143 0 : t.result = -1;
144 : }
145 : }
146 0 : }
147 :
148 0 : void cleanuptimers()
149 : {
150 0 : for(const timer& t : timers)
151 : {
152 0 : if(t.gpu)
153 : {
154 0 : glDeleteQueries(timer::Timer_MaxQuery, t.query.data());
155 : }
156 : }
157 0 : timers.clear();
158 0 : timerorder.clear();
159 0 : }
160 :
161 0 : void printtimers(int conw, int framemillis)
162 : {
163 0 : if(!frametimer && !usetimers)
164 : {
165 0 : return;
166 : }
167 : static int lastprint = 0;
168 0 : int offset = 0;
169 0 : if(frametimer)
170 : {
171 : static int printmillis = 0;
172 0 : if(totalmillis - lastprint >= 200)
173 : {
174 0 : printmillis = framemillis;
175 : }
176 : std::array<char, 200> framestring;
177 0 : constexpr int size = 42;
178 0 : std::sprintf(framestring.data(), "frame time %i ms", printmillis);
179 0 : ttr.fontsize(size);
180 0 : ttr.renderttf(framestring.data(), {0xFF, 0xFF, 0xFF, 0}, conw-20*size, size*3/2+offset*9*size/8);
181 : //draw_textf("frame time %i ms", conw-20*FONTH, conh-FONTH*3/2-offset*9*FONTH/8, printmillis);
182 0 : offset++;
183 : }
184 0 : if(usetimers)
185 : {
186 0 : for(int i : timerorder)
187 : {
188 0 : timer &t = timers[i];
189 0 : if(t.print < 0 ? t.result >= 0 : totalmillis - lastprint >= 200)
190 : {
191 0 : t.print = t.result;
192 : }
193 0 : if(t.print < 0 || (t.gpu && !(t.waiting&(1<<timercycle))))
194 : {
195 0 : continue;
196 : }
197 : std::array<char, 200> framestring;
198 0 : constexpr int size = 42;
199 0 : std::sprintf(framestring.data(), "%s%s %5.2f ms", t.name, t.gpu ? "" : " (cpu)", t.print);
200 0 : ttr.fontsize(size);
201 0 : ttr.renderttf(framestring.data(), {0xFF, 0xFF, 0xFF, 0}, conw-20*size, size*3/2+offset*9*size/8);
202 :
203 : //draw_textf("%s%s %5.2f ms", conw-20*FONTH, conh-FONTH*3/2-offset*9*FONTH/8, t.name, t.gpu ? "" : " (cpu)", t.print);
204 0 : offset++;
205 : }
206 : }
207 0 : if(totalmillis - lastprint >= 200)
208 : {
209 0 : lastprint = totalmillis;
210 : }
211 : }
|