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