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