1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 *
16 * Copyright 2016, Blender Foundation.
17 */
18
19 /** \file
20 * \ingroup draw
21 */
22
23 #include "BLI_listbase.h"
24 #include "BLI_rect.h"
25 #include "BLI_string.h"
26
27 #include "BKE_global.h"
28
29 #include "BLF_api.h"
30
31 #include "MEM_guardedalloc.h"
32
33 #include "draw_manager.h"
34
35 #include "GPU_debug.h"
36 #include "GPU_texture.h"
37
38 #include "UI_resources.h"
39
40 #include "draw_manager_profiling.h"
41
42 #define MAX_TIMER_NAME 32
43 #define MAX_NESTED_TIMER 8
44 #define CHUNK_SIZE 8
45 #define GPU_TIMER_FALLOFF 0.1
46
47 typedef struct DRWTimer {
48 uint32_t query[2];
49 uint64_t time_average;
50 char name[MAX_TIMER_NAME];
51 int lvl; /* Hierarchy level for nested timer. */
52 bool is_query; /* Does this timer actually perform queries or is it just a group. */
53 } DRWTimer;
54
55 static struct DRWTimerPool {
56 DRWTimer *timers;
57 int chunk_count; /* Number of chunk allocated. */
58 int timer_count; /* chunk_count * CHUNK_SIZE */
59 int timer_increment; /* Keep track of where we are in the stack. */
60 int end_increment; /* Keep track of bad usage. */
61 bool is_recording; /* Are we in the render loop? */
62 bool is_querying; /* Keep track of bad usage. */
63 } DTP = {NULL};
64
DRW_stats_free(void)65 void DRW_stats_free(void)
66 {
67 if (DTP.timers != NULL) {
68 // for (int i = 0; i < DTP.timer_count; i++) {
69 // DRWTimer *timer = &DTP.timers[i];
70 // glDeleteQueries(2, timer->query);
71 // }
72 MEM_freeN(DTP.timers);
73 DTP.timers = NULL;
74 }
75 }
76
DRW_stats_begin(void)77 void DRW_stats_begin(void)
78 {
79 if (G.debug_value > 20 && G.debug_value < 30) {
80 DTP.is_recording = true;
81 }
82
83 if (DTP.is_recording && DTP.timers == NULL) {
84 DTP.chunk_count = 1;
85 DTP.timer_count = DTP.chunk_count * CHUNK_SIZE;
86 DTP.timers = MEM_callocN(sizeof(DRWTimer) * DTP.timer_count, "DRWTimer stack");
87 }
88 else if (!DTP.is_recording && DTP.timers != NULL) {
89 DRW_stats_free();
90 }
91
92 DTP.is_querying = false;
93 DTP.timer_increment = 0;
94 DTP.end_increment = 0;
95 }
96
drw_stats_timer_get(void)97 static DRWTimer *drw_stats_timer_get(void)
98 {
99 if (UNLIKELY(DTP.timer_increment >= DTP.timer_count)) {
100 /* Resize the stack. */
101 DTP.chunk_count++;
102 DTP.timer_count = DTP.chunk_count * CHUNK_SIZE;
103 DTP.timers = MEM_recallocN(DTP.timers, sizeof(DRWTimer) * DTP.timer_count);
104 }
105
106 return &DTP.timers[DTP.timer_increment++];
107 }
108
drw_stats_timer_start_ex(const char * name,const bool is_query)109 static void drw_stats_timer_start_ex(const char *name, const bool is_query)
110 {
111 if (DTP.is_recording) {
112 DRWTimer *timer = drw_stats_timer_get();
113 BLI_strncpy(timer->name, name, MAX_TIMER_NAME);
114 timer->lvl = DTP.timer_increment - DTP.end_increment - 1;
115 timer->is_query = is_query;
116
117 /* Queries cannot be nested or interleaved. */
118 BLI_assert(!DTP.is_querying);
119 if (timer->is_query) {
120 if (timer->query[0] == 0) {
121 // glGenQueries(1, timer->query);
122 }
123
124 // glFinish();
125 /* Issue query for the next frame */
126 // glBeginQuery(GL_TIME_ELAPSED, timer->query[0]);
127 DTP.is_querying = true;
128 }
129 }
130 }
131
132 /* Use this to group the queries. It does NOT keep track
133 * of the time, it only sum what the queries inside it. */
DRW_stats_group_start(const char * name)134 void DRW_stats_group_start(const char *name)
135 {
136 drw_stats_timer_start_ex(name, false);
137
138 GPU_debug_group_begin(name);
139 }
140
DRW_stats_group_end(void)141 void DRW_stats_group_end(void)
142 {
143 GPU_debug_group_end();
144 if (DTP.is_recording) {
145 BLI_assert(!DTP.is_querying);
146 DTP.end_increment++;
147 }
148 }
149
150 /* NOTE: Only call this when no sub timer will be called. */
DRW_stats_query_start(const char * name)151 void DRW_stats_query_start(const char *name)
152 {
153 GPU_debug_group_begin(name);
154 drw_stats_timer_start_ex(name, false);
155 drw_stats_timer_start_ex(name, true);
156 }
157
DRW_stats_query_end(void)158 void DRW_stats_query_end(void)
159 {
160 GPU_debug_group_end();
161 if (DTP.is_recording) {
162 DTP.end_increment++;
163 BLI_assert(DTP.is_querying);
164 // glEndQuery(GL_TIME_ELAPSED);
165 DTP.is_querying = false;
166 }
167 }
168
DRW_stats_reset(void)169 void DRW_stats_reset(void)
170 {
171 BLI_assert((DTP.timer_increment - DTP.end_increment) <= 0 &&
172 "You forgot a DRW_stats_group/query_end somewhere!");
173 BLI_assert((DTP.timer_increment - DTP.end_increment) >= 0 &&
174 "You forgot a DRW_stats_group/query_start somewhere!");
175
176 if (DTP.is_recording) {
177 uint64_t lvl_time[MAX_NESTED_TIMER] = {0};
178
179 /* Swap queries for the next frame and sum up each lvl time. */
180 for (int i = DTP.timer_increment - 1; i >= 0; i--) {
181 DRWTimer *timer = &DTP.timers[i];
182 SWAP(uint32_t, timer->query[0], timer->query[1]);
183
184 BLI_assert(timer->lvl < MAX_NESTED_TIMER);
185
186 if (timer->is_query) {
187 uint64_t time = 0;
188 if (timer->query[0] != 0) {
189 // glGetQueryObjectui64v(timer->query[0], GL_QUERY_RESULT, &time);
190 }
191 else {
192 time = 1000000000; /* 1ms default */
193 }
194
195 timer->time_average = timer->time_average * (1.0 - GPU_TIMER_FALLOFF) +
196 time * GPU_TIMER_FALLOFF;
197 timer->time_average = MIN2(timer->time_average, 1000000000);
198 }
199 else {
200 timer->time_average = lvl_time[timer->lvl + 1];
201 lvl_time[timer->lvl + 1] = 0;
202 }
203
204 lvl_time[timer->lvl] += timer->time_average;
205 }
206
207 DTP.is_recording = false;
208 }
209 }
210
draw_stat_5row(const rcti * rect,int u,int v,const char * txt,const int size)211 static void draw_stat_5row(const rcti *rect, int u, int v, const char *txt, const int size)
212 {
213 BLF_draw_default_ascii(rect->xmin + (1 + u * 5) * U.widget_unit,
214 rect->ymax - (3 + v) * U.widget_unit,
215 0.0f,
216 txt,
217 size);
218 }
219
draw_stat(const rcti * rect,int u,int v,const char * txt,const int size)220 static void draw_stat(const rcti *rect, int u, int v, const char *txt, const int size)
221 {
222 BLF_draw_default_ascii(
223 rect->xmin + (1 + u) * U.widget_unit, rect->ymax - (3 + v) * U.widget_unit, 0.0f, txt, size);
224 }
225
DRW_stats_draw(const rcti * rect)226 void DRW_stats_draw(const rcti *rect)
227 {
228 char stat_string[64];
229 int lvl_index[MAX_NESTED_TIMER];
230 int v = 0, u = 0;
231
232 double init_tot_time = 0.0, background_tot_time = 0.0, render_tot_time = 0.0, tot_time = 0.0;
233
234 int fontid = BLF_default();
235 UI_FontThemeColor(fontid, TH_TEXT_HI);
236 BLF_enable(fontid, BLF_SHADOW);
237 BLF_shadow(fontid, 5, (const float[4]){0.0f, 0.0f, 0.0f, 0.75f});
238 BLF_shadow_offset(fontid, 0, -1);
239
240 BLF_batch_draw_begin();
241
242 /* ------------------------------------------ */
243 /* ---------------- CPU stats --------------- */
244 /* ------------------------------------------ */
245 /* Label row */
246 char col_label[32];
247 sprintf(col_label, "Engine");
248 draw_stat_5row(rect, u++, v, col_label, sizeof(col_label));
249 sprintf(col_label, "Init");
250 draw_stat_5row(rect, u++, v, col_label, sizeof(col_label));
251 sprintf(col_label, "Background");
252 draw_stat_5row(rect, u++, v, col_label, sizeof(col_label));
253 sprintf(col_label, "Render");
254 draw_stat_5row(rect, u++, v, col_label, sizeof(col_label));
255 sprintf(col_label, "Total (w/o cache)");
256 draw_stat_5row(rect, u++, v, col_label, sizeof(col_label));
257 v++;
258
259 /* Engines rows */
260 char time_to_txt[16];
261 LISTBASE_FOREACH (LinkData *, link, &DST.enabled_engines) {
262 u = 0;
263 DrawEngineType *engine = link->data;
264 ViewportEngineData *data = drw_viewport_engine_data_ensure(engine);
265
266 draw_stat_5row(rect, u++, v, engine->idname, sizeof(engine->idname));
267
268 init_tot_time += data->init_time;
269 sprintf(time_to_txt, "%.2fms", data->init_time);
270 draw_stat_5row(rect, u++, v, time_to_txt, sizeof(time_to_txt));
271
272 background_tot_time += data->background_time;
273 sprintf(time_to_txt, "%.2fms", data->background_time);
274 draw_stat_5row(rect, u++, v, time_to_txt, sizeof(time_to_txt));
275
276 render_tot_time += data->render_time;
277 sprintf(time_to_txt, "%.2fms", data->render_time);
278 draw_stat_5row(rect, u++, v, time_to_txt, sizeof(time_to_txt));
279
280 tot_time += data->init_time + data->background_time + data->render_time;
281 sprintf(time_to_txt, "%.2fms", data->init_time + data->background_time + data->render_time);
282 draw_stat_5row(rect, u++, v, time_to_txt, sizeof(time_to_txt));
283 v++;
284 }
285
286 /* Totals row */
287 u = 0;
288 sprintf(col_label, "Sub Total");
289 draw_stat_5row(rect, u++, v, col_label, sizeof(col_label));
290 sprintf(time_to_txt, "%.2fms", init_tot_time);
291 draw_stat_5row(rect, u++, v, time_to_txt, sizeof(time_to_txt));
292 sprintf(time_to_txt, "%.2fms", background_tot_time);
293 draw_stat_5row(rect, u++, v, time_to_txt, sizeof(time_to_txt));
294 sprintf(time_to_txt, "%.2fms", render_tot_time);
295 draw_stat_5row(rect, u++, v, time_to_txt, sizeof(time_to_txt));
296 sprintf(time_to_txt, "%.2fms", tot_time);
297 draw_stat_5row(rect, u++, v, time_to_txt, sizeof(time_to_txt));
298 v += 2;
299
300 u = 0;
301 double *cache_time = GPU_viewport_cache_time_get(DST.viewport);
302 sprintf(col_label, "Cache Time");
303 draw_stat_5row(rect, u++, v, col_label, sizeof(col_label));
304 sprintf(time_to_txt, "%.2fms", *cache_time);
305 draw_stat_5row(rect, u++, v, time_to_txt, sizeof(time_to_txt));
306 v += 2;
307
308 /* ------------------------------------------ */
309 /* ---------------- GPU stats --------------- */
310 /* ------------------------------------------ */
311
312 /* Memory Stats */
313 uint tex_mem = GPU_texture_memory_usage_get();
314 uint vbo_mem = GPU_vertbuf_get_memory_usage();
315
316 sprintf(stat_string, "GPU Memory");
317 draw_stat(rect, 0, v, stat_string, sizeof(stat_string));
318 sprintf(stat_string, "%.2fMB", (double)(tex_mem + vbo_mem) / 1000000.0);
319 draw_stat_5row(rect, 1, v++, stat_string, sizeof(stat_string));
320 sprintf(stat_string, "Textures");
321 draw_stat(rect, 1, v, stat_string, sizeof(stat_string));
322 sprintf(stat_string, "%.2fMB", (double)tex_mem / 1000000.0);
323 draw_stat_5row(rect, 1, v++, stat_string, sizeof(stat_string));
324 sprintf(stat_string, "Meshes");
325 draw_stat(rect, 1, v, stat_string, sizeof(stat_string));
326 sprintf(stat_string, "%.2fMB", (double)vbo_mem / 1000000.0);
327 draw_stat_5row(rect, 1, v++, stat_string, sizeof(stat_string));
328 v += 1;
329
330 /* GPU Timings */
331 BLI_strncpy(stat_string, "GPU Render Timings", sizeof(stat_string));
332 draw_stat(rect, 0, v++, stat_string, sizeof(stat_string));
333
334 for (int i = 0; i < DTP.timer_increment; i++) {
335 double time_ms, time_percent;
336 DRWTimer *timer = &DTP.timers[i];
337 DRWTimer *timer_parent = (timer->lvl > 0) ? &DTP.timers[lvl_index[timer->lvl - 1]] : NULL;
338
339 /* Only display a number of lvl at a time */
340 if ((G.debug_value - 21) < timer->lvl) {
341 continue;
342 }
343
344 BLI_assert(timer->lvl < MAX_NESTED_TIMER);
345 lvl_index[timer->lvl] = i;
346
347 time_ms = timer->time_average / 1000000.0;
348
349 if (timer_parent != NULL) {
350 time_percent = ((double)timer->time_average / (double)timer_parent->time_average) * 100.0;
351 }
352 else {
353 time_percent = 100.0;
354 }
355
356 /* avoid very long number */
357 time_ms = MIN2(time_ms, 999.0);
358 time_percent = MIN2(time_percent, 100.0);
359
360 BLI_snprintf(stat_string, sizeof(stat_string), "%s", timer->name);
361 draw_stat(rect, 0 + timer->lvl, v, stat_string, sizeof(stat_string));
362 BLI_snprintf(stat_string, sizeof(stat_string), "%.2fms", time_ms);
363 draw_stat(rect, 12 + timer->lvl, v, stat_string, sizeof(stat_string));
364 BLI_snprintf(stat_string, sizeof(stat_string), "%.0f", time_percent);
365 draw_stat(rect, 16 + timer->lvl, v, stat_string, sizeof(stat_string));
366 v++;
367 }
368
369 BLF_batch_draw_end();
370 BLF_disable(fontid, BLF_SHADOW);
371 }
372