Blender  V3.3
draw_manager_profiling.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2016 Blender Foundation. */
3 
8 #include "BLI_listbase.h"
9 #include "BLI_rect.h"
10 #include "BLI_string.h"
11 
12 #include "BKE_global.h"
13 
14 #include "BLF_api.h"
15 
16 #include "MEM_guardedalloc.h"
17 
18 #include "draw_manager.h"
19 
20 #include "GPU_debug.h"
21 #include "GPU_texture.h"
22 
23 #include "UI_resources.h"
24 
25 #include "draw_manager_profiling.h"
26 
27 #define MAX_TIMER_NAME 32
28 #define MAX_NESTED_TIMER 8
29 #define MIM_RANGE_LEN 8
30 #define GPU_TIMER_FALLOFF 0.1
31 
32 typedef struct DRWTimer {
36  int lvl; /* Hierarchy level for nested timer. */
37  bool is_query; /* Does this timer actually perform queries or is it just a group. */
39 
40 static struct DRWTimerPool {
42  int chunk_count; /* Number of chunk allocated. */
43  int timer_count; /* chunk_count * CHUNK_SIZE */
44  int timer_increment; /* Keep track of where we are in the stack. */
45  int end_increment; /* Keep track of bad usage. */
46  bool is_recording; /* Are we in the render loop? */
47  bool is_querying; /* Keep track of bad usage. */
48 } DTP = {NULL};
49 
50 void DRW_stats_free(void)
51 {
52  if (DTP.timers != NULL) {
53  // for (int i = 0; i < DTP.timer_count; i++) {
54  // DRWTimer *timer = &DTP.timers[i];
55  // glDeleteQueries(2, timer->query);
56  // }
58  DTP.timers = NULL;
59  }
60 }
61 
62 void DRW_stats_begin(void)
63 {
64  if (G.debug_value > 20 && G.debug_value < 30) {
65  DTP.is_recording = true;
66  }
67 
68  if (DTP.is_recording && DTP.timers == NULL) {
69  DTP.chunk_count = 1;
71  DTP.timers = MEM_callocN(sizeof(DRWTimer) * DTP.timer_count, "DRWTimer stack");
72  }
73  else if (!DTP.is_recording && DTP.timers != NULL) {
75  }
76 
77  DTP.is_querying = false;
78  DTP.timer_increment = 0;
79  DTP.end_increment = 0;
80 }
81 
83 {
85  /* Resize the stack. */
86  DTP.chunk_count++;
89  }
90 
91  return &DTP.timers[DTP.timer_increment++];
92 }
93 
94 static void drw_stats_timer_start_ex(const char *name, const bool is_query)
95 {
96  if (DTP.is_recording) {
97  DRWTimer *timer = drw_stats_timer_get();
98  BLI_strncpy(timer->name, name, MAX_TIMER_NAME);
99  timer->lvl = DTP.timer_increment - DTP.end_increment - 1;
100  timer->is_query = is_query;
101 
102  /* Queries cannot be nested or interleaved. */
104  if (timer->is_query) {
105  if (timer->query[0] == 0) {
106  // glGenQueries(1, timer->query);
107  }
108 
109  // glFinish();
110  /* Issue query for the next frame */
111  // glBeginQuery(GL_TIME_ELAPSED, timer->query[0]);
112  DTP.is_querying = true;
113  }
114  }
115 }
116 
117 void DRW_stats_group_start(const char *name)
118 {
119  drw_stats_timer_start_ex(name, false);
120 
121  GPU_debug_group_begin(name);
122 }
123 
125 {
127  if (DTP.is_recording) {
129  DTP.end_increment++;
130  }
131 }
132 
133 void DRW_stats_query_start(const char *name)
134 {
135  GPU_debug_group_begin(name);
136  drw_stats_timer_start_ex(name, true);
137 }
138 
140 {
142  if (DTP.is_recording) {
143  DTP.end_increment++;
145  // glEndQuery(GL_TIME_ELAPSED);
146  DTP.is_querying = false;
147  }
148 }
149 
150 void DRW_stats_reset(void)
151 {
153  "You forgot a DRW_stats_group/query_end somewhere!");
155  "You forgot a DRW_stats_group/query_start somewhere!");
156 
157  if (DTP.is_recording) {
158  uint64_t lvl_time[MAX_NESTED_TIMER] = {0};
159 
160  /* Swap queries for the next frame and sum up each lvl time. */
161  for (int i = DTP.timer_increment - 1; i >= 0; i--) {
162  DRWTimer *timer = &DTP.timers[i];
163  SWAP(uint32_t, timer->query[0], timer->query[1]);
164 
165  BLI_assert(timer->lvl < MAX_NESTED_TIMER);
166 
167  if (timer->is_query) {
168  uint64_t time = 0;
169  if (timer->query[0] != 0) {
170  // glGetQueryObjectui64v(timer->query[0], GL_QUERY_RESULT, &time);
171  }
172  else {
173  time = 1000000000; /* 1ms default */
174  }
175 
176  timer->time_average = timer->time_average * (1.0 - GPU_TIMER_FALLOFF) +
178  timer->time_average = MIN2(timer->time_average, 1000000000);
179  }
180  else {
181  timer->time_average = lvl_time[timer->lvl + 1];
182  lvl_time[timer->lvl + 1] = 0;
183  }
184 
185  lvl_time[timer->lvl] += timer->time_average;
186  }
187 
188  DTP.is_recording = false;
189  }
190 }
191 
192 static void draw_stat_5row(const rcti *rect, int u, int v, const char *txt, const int size)
193 {
194  BLF_draw_default(rect->xmin + (1 + u * 5) * U.widget_unit,
195  rect->ymax - (3 + v) * U.widget_unit,
196  0.0f,
197  txt,
198  size);
199 }
200 
201 static void draw_stat(const rcti *rect, int u, int v, const char *txt, const int size)
202 {
204  rect->xmin + (1 + u) * U.widget_unit, rect->ymax - (3 + v) * U.widget_unit, 0.0f, txt, size);
205 }
206 
207 void DRW_stats_draw(const rcti *rect)
208 {
209  char stat_string[64];
210  int lvl_index[MAX_NESTED_TIMER];
211  int v = 0, u = 0;
212 
213  double init_tot_time = 0.0, background_tot_time = 0.0, render_tot_time = 0.0, tot_time = 0.0;
214 
215  int fontid = BLF_default();
216  UI_FontThemeColor(fontid, TH_TEXT_HI);
217  BLF_enable(fontid, BLF_SHADOW);
218  BLF_shadow(fontid, 5, (const float[4]){0.0f, 0.0f, 0.0f, 0.75f});
219  BLF_shadow_offset(fontid, 0, -1);
220 
222 
223  /* ------------------------------------------ */
224  /* ---------------- CPU stats --------------- */
225  /* ------------------------------------------ */
226  /* Label row */
227  char col_label[32];
228  sprintf(col_label, "Engine");
229  draw_stat_5row(rect, u++, v, col_label, sizeof(col_label));
230  sprintf(col_label, "Init");
231  draw_stat_5row(rect, u++, v, col_label, sizeof(col_label));
232  sprintf(col_label, "Background");
233  draw_stat_5row(rect, u++, v, col_label, sizeof(col_label));
234  sprintf(col_label, "Render");
235  draw_stat_5row(rect, u++, v, col_label, sizeof(col_label));
236  sprintf(col_label, "Total (w/o cache)");
237  draw_stat_5row(rect, u++, v, col_label, sizeof(col_label));
238  v++;
239 
240  /* Engines rows */
241  char time_to_txt[16];
243  u = 0;
244 
245  draw_stat_5row(rect, u++, v, engine->idname, sizeof(engine->idname));
246 
247  init_tot_time += data->init_time;
248  sprintf(time_to_txt, "%.2fms", data->init_time);
249  draw_stat_5row(rect, u++, v, time_to_txt, sizeof(time_to_txt));
250 
251  background_tot_time += data->background_time;
252  sprintf(time_to_txt, "%.2fms", data->background_time);
253  draw_stat_5row(rect, u++, v, time_to_txt, sizeof(time_to_txt));
254 
255  render_tot_time += data->render_time;
256  sprintf(time_to_txt, "%.2fms", data->render_time);
257  draw_stat_5row(rect, u++, v, time_to_txt, sizeof(time_to_txt));
258 
259  tot_time += data->init_time + data->background_time + data->render_time;
260  sprintf(time_to_txt, "%.2fms", data->init_time + data->background_time + data->render_time);
261  draw_stat_5row(rect, u++, v, time_to_txt, sizeof(time_to_txt));
262  v++;
263  }
264 
265  /* Totals row */
266  u = 0;
267  sprintf(col_label, "Sub Total");
268  draw_stat_5row(rect, u++, v, col_label, sizeof(col_label));
269  sprintf(time_to_txt, "%.2fms", init_tot_time);
270  draw_stat_5row(rect, u++, v, time_to_txt, sizeof(time_to_txt));
271  sprintf(time_to_txt, "%.2fms", background_tot_time);
272  draw_stat_5row(rect, u++, v, time_to_txt, sizeof(time_to_txt));
273  sprintf(time_to_txt, "%.2fms", render_tot_time);
274  draw_stat_5row(rect, u++, v, time_to_txt, sizeof(time_to_txt));
275  sprintf(time_to_txt, "%.2fms", tot_time);
276  draw_stat_5row(rect, u++, v, time_to_txt, sizeof(time_to_txt));
277  v += 2;
278 
279  u = 0;
280  double *cache_time = DRW_view_data_cache_time_get(DST.view_data_active);
281  sprintf(col_label, "Cache Time");
282  draw_stat_5row(rect, u++, v, col_label, sizeof(col_label));
283  sprintf(time_to_txt, "%.2fms", *cache_time);
284  draw_stat_5row(rect, u++, v, time_to_txt, sizeof(time_to_txt));
285  v += 2;
286 
287  /* ------------------------------------------ */
288  /* ---------------- GPU stats --------------- */
289  /* ------------------------------------------ */
290 
291  /* Memory Stats */
294 
295  sprintf(stat_string, "GPU Memory");
296  draw_stat(rect, 0, v, stat_string, sizeof(stat_string));
297  sprintf(stat_string, "%.2fMB", (double)(tex_mem + vbo_mem) / 1000000.0);
298  draw_stat_5row(rect, 1, v++, stat_string, sizeof(stat_string));
299  sprintf(stat_string, "Textures");
300  draw_stat(rect, 1, v, stat_string, sizeof(stat_string));
301  sprintf(stat_string, "%.2fMB", (double)tex_mem / 1000000.0);
302  draw_stat_5row(rect, 1, v++, stat_string, sizeof(stat_string));
303  sprintf(stat_string, "Meshes");
304  draw_stat(rect, 1, v, stat_string, sizeof(stat_string));
305  sprintf(stat_string, "%.2fMB", (double)vbo_mem / 1000000.0);
306  draw_stat_5row(rect, 1, v++, stat_string, sizeof(stat_string));
307  v += 1;
308 
309  /* GPU Timings */
310  BLI_strncpy(stat_string, "GPU Render Timings", sizeof(stat_string));
311  draw_stat(rect, 0, v++, stat_string, sizeof(stat_string));
312 
313  for (int i = 0; i < DTP.timer_increment; i++) {
314  double time_ms, time_percent;
315  DRWTimer *timer = &DTP.timers[i];
316  DRWTimer *timer_parent = (timer->lvl > 0) ? &DTP.timers[lvl_index[timer->lvl - 1]] : NULL;
317 
318  /* Only display a number of lvl at a time */
319  if ((G.debug_value - 21) < timer->lvl) {
320  continue;
321  }
322 
323  BLI_assert(timer->lvl < MAX_NESTED_TIMER);
324  lvl_index[timer->lvl] = i;
325 
326  time_ms = timer->time_average / 1000000.0;
327 
328  if (timer_parent != NULL) {
329  time_percent = ((double)timer->time_average / (double)timer_parent->time_average) * 100.0;
330  }
331  else {
332  time_percent = 100.0;
333  }
334 
335  /* avoid very long number */
336  time_ms = MIN2(time_ms, 999.0);
337  time_percent = MIN2(time_percent, 100.0);
338 
339  BLI_snprintf(stat_string, sizeof(stat_string), "%s", timer->name);
340  draw_stat(rect, 0 + timer->lvl, v, stat_string, sizeof(stat_string));
341  BLI_snprintf(stat_string, sizeof(stat_string), "%.2fms", time_ms);
342  draw_stat(rect, 12 + timer->lvl, v, stat_string, sizeof(stat_string));
343  BLI_snprintf(stat_string, sizeof(stat_string), "%.0f", time_percent);
344  draw_stat(rect, 16 + timer->lvl, v, stat_string, sizeof(stat_string));
345  v++;
346  }
347 
349  BLF_disable(fontid, BLF_SHADOW);
350 }
@ BLF_SHADOW
Definition: BLF_api.h:336
int BLF_default(void)
Definition: blf_default.c:44
void BLF_shadow_offset(int fontid, int x, int y)
Definition: blf.c:806
void BLF_shadow(int fontid, int level, const float rgba[4]) ATTR_NONNULL(3)
Definition: blf.c:796
void BLF_disable(int fontid, int option)
Definition: blf.c:279
void BLF_batch_draw_begin(void)
Definition: blf.c:466
void BLF_enable(int fontid, int option)
Definition: blf.c:270
void BLF_batch_draw_end(void)
Definition: blf.c:479
void BLF_draw_default(float x, float y, float z, const char *str, size_t str_len) ATTR_NONNULL()
Definition: blf_default.c:59
#define BLI_assert(a)
Definition: BLI_assert.h:46
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t maxncpy) ATTR_NONNULL()
Definition: string.c:64
size_t BLI_snprintf(char *__restrict dst, size_t maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
unsigned int uint
Definition: BLI_sys_types.h:67
#define SWAP(type, a, b)
#define UNLIKELY(x)
#define MIN2(a, b)
typedef double(DMatrix)[4][4]
void GPU_debug_group_end(void)
Definition: gpu_debug.cc:32
void GPU_debug_group_begin(const char *name)
Definition: gpu_debug.cc:21
unsigned int GPU_texture_memory_usage_get(void)
Definition: gpu_texture.cc:227
uint GPU_vertbuf_get_memory_usage(void)
Read Guarded memory(de)allocation.
#define MEM_recallocN(vmemh, len)
@ TH_TEXT_HI
Definition: UI_resources.h:43
void UI_FontThemeColor(int fontid, int colorid)
Definition: resources.c:1134
ATTR_WARN_UNUSED_RESULT const BMVert * v
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition: btDbvt.cpp:52
unsigned int U
Definition: btGjkEpa3.h:78
double time
DRWManager DST
Definition: draw_manager.c:104
void DRW_stats_query_end(void)
void DRW_stats_begin(void)
#define MAX_NESTED_TIMER
#define MAX_TIMER_NAME
void DRW_stats_group_start(const char *name)
void DRW_stats_query_start(const char *name)
static struct DRWTimerPool DTP
void DRW_stats_reset(void)
void DRW_stats_draw(const rcti *rect)
static void drw_stats_timer_start_ex(const char *name, const bool is_query)
struct DRWTimer DRWTimer
static DRWTimer * drw_stats_timer_get(void)
#define GPU_TIMER_FALLOFF
void DRW_stats_group_end(void)
static void draw_stat_5row(const rcti *rect, int u, int v, const char *txt, const int size)
void DRW_stats_free(void)
static void draw_stat(const rcti *rect, int u, int v, const char *txt, const int size)
#define MIM_RANGE_LEN
double * DRW_view_data_cache_time_get(DRWViewData *view_data)
#define DRW_ENABLED_ENGINE_ITER(view_data_, engine_, data_)
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:31
#define G(x, y, z)
unsigned int uint32_t
Definition: stdint.h:80
unsigned __int64 uint64_t
Definition: stdint.h:90
struct DRWViewData * view_data_active
Definition: draw_manager.h:564
char name[MAX_TIMER_NAME]
uint64_t time_average
uint32_t query[2]
int ymax
Definition: DNA_vec_types.h:64
int xmin
Definition: DNA_vec_types.h:63