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