Merge branch 'master' into blender2.8
[blender.git] / source / blender / depsgraph / intern / eval / deg_eval_debug.cc
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  * The Original Code is Copyright (C) 2014 Blender Foundation.
19  * All rights reserved.
20  *
21  * Original Author: Lukas Toenne
22  * Contributor(s): None Yet
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/depsgraph/intern/eval/deg_eval_debug.cc
28  *  \ingroup depsgraph
29  *
30  * Implementation of tools for debugging the depsgraph
31  */
32
33 #include "intern/eval/deg_eval_debug.h"
34
35 #include <cstring>  /* required for STREQ later on. */
36
37 #include "BLI_listbase.h"
38 #include "BLI_ghash.h"
39
40 extern "C" {
41 #include "WM_api.h"
42 #include "WM_types.h"
43 }  /* extern "C" */
44
45 #include "DEG_depsgraph_debug.h"
46
47 #include "intern/nodes/deg_node.h"
48 #include "intern/nodes/deg_node_component.h"
49 #include "intern/nodes/deg_node_operation.h"
50 #include "intern/depsgraph_intern.h"
51
52 namespace DEG {
53
54 DepsgraphStats *DepsgraphDebug::stats = NULL;
55
56 static string get_component_name(eDepsNode_Type type, const char *name = "")
57 {
58         DepsNodeFactory *factory = deg_get_node_factory(type);
59         if (name[0] != '\0') {
60                 return string(factory->tname());
61         }
62         else {
63                 return string(factory->tname()) + " | " + name;
64         }
65 }
66
67 static void times_clear(DepsgraphStatsTimes &times)
68 {
69         times.duration_last = 0.0f;
70 }
71
72 static void times_add(DepsgraphStatsTimes &times, float time)
73 {
74         times.duration_last += time;
75 }
76
77 void DepsgraphDebug::eval_begin(const EvaluationContext *UNUSED(eval_ctx))
78 {
79         /* TODO(sergey): Stats are currently globally disabled. */
80         /* verify_stats(); */
81         reset_stats();
82 }
83
84 void DepsgraphDebug::eval_end(const EvaluationContext *UNUSED(eval_ctx))
85 {
86         WM_main_add_notifier(NC_SPACE | ND_SPACE_INFO_REPORT, NULL);
87 }
88
89 void DepsgraphDebug::eval_step(const EvaluationContext *UNUSED(eval_ctx),
90                                const char *message)
91 {
92 #ifdef DEG_DEBUG_BUILD
93         if (deg_debug_eval_cb)
94                 deg_debug_eval_cb(deg_debug_eval_userdata, message);
95 #else
96         (void)message;  /* Ignored. */
97 #endif
98 }
99
100 void DepsgraphDebug::task_started(Depsgraph *graph,
101                                   const OperationDepsNode *node)
102 {
103         if (stats) {
104                 BLI_spin_lock(&graph->lock);
105
106                 ComponentDepsNode *comp = node->owner;
107                 ID *id = comp->owner->id_orig;
108
109                 DepsgraphStatsID *id_stats = get_id_stats(id, true);
110                 times_clear(id_stats->times);
111
112                 /* XXX TODO use something like: if (id->flag & ID_DEG_DETAILS) {...} */
113                 if (0) {
114                         /* XXX component name usage needs cleanup! currently mixes identifier
115                          * and description strings!
116                          */
117                         DepsgraphStatsComponent *comp_stats =
118                                 get_component_stats(id, get_component_name(comp->type,
119                                                                            comp->name).c_str(),
120                                                     true);
121                         times_clear(comp_stats->times);
122                 }
123
124                 BLI_spin_unlock(&graph->lock);
125         }
126 }
127
128 void DepsgraphDebug::task_completed(Depsgraph *graph,
129                                     const OperationDepsNode *node,
130                                     double time)
131 {
132         if (stats) {
133                 BLI_spin_lock(&graph->lock);
134
135                 ComponentDepsNode *comp = node->owner;
136                 ID *id = comp->owner->id_orig;
137
138                 DepsgraphStatsID *id_stats = get_id_stats(id, true);
139                 times_add(id_stats->times, time);
140
141                 /* XXX TODO use something like: if (id->flag & ID_DEG_DETAILS) {...} */
142                 if (0) {
143                         /* XXX component name usage needs cleanup! currently mixes identifier
144                          * and description strings!
145                          */
146                         DepsgraphStatsComponent *comp_stats =
147                                 get_component_stats(id,
148                                                     get_component_name(comp->type,
149                                                                        comp->name).c_str(),
150                                                     true);
151                         times_add(comp_stats->times, time);
152                 }
153
154                 BLI_spin_unlock(&graph->lock);
155         }
156 }
157
158 /* ********** */
159 /* Statistics */
160
161
162 /* GHash callback */
163 static void deg_id_stats_free(void *val)
164 {
165         DepsgraphStatsID *id_stats = (DepsgraphStatsID *)val;
166
167         if (id_stats) {
168                 BLI_freelistN(&id_stats->components);
169                 MEM_freeN(id_stats);
170         }
171 }
172
173 void DepsgraphDebug::stats_init()
174 {
175         if (!stats) {
176                 stats = (DepsgraphStats *)MEM_callocN(sizeof(DepsgraphStats),
177                                                       "Depsgraph Stats");
178                 stats->id_stats = BLI_ghash_new(BLI_ghashutil_ptrhash,
179                                                 BLI_ghashutil_ptrcmp,
180                                                 "Depsgraph ID Stats Hash");
181         }
182 }
183
184 void DepsgraphDebug::stats_free()
185 {
186         if (stats) {
187                 BLI_ghash_free(stats->id_stats, NULL, deg_id_stats_free);
188                 MEM_freeN(stats);
189                 stats = NULL;
190         }
191 }
192
193 void DepsgraphDebug::verify_stats()
194 {
195         stats_init();
196 }
197
198 void DepsgraphDebug::reset_stats()
199 {
200         if (!stats) {
201                 return;
202         }
203
204         /* XXX this doesn't work, will immediately clear all info,
205          * since most depsgraph updates have none or very few updates to handle.
206          *
207          * Could consider clearing only zero-user ID blocks here
208          */
209 //      BLI_ghash_clear(stats->id_stats, NULL, deg_id_stats_free);
210 }
211
212 DepsgraphStatsID *DepsgraphDebug::get_id_stats(ID *id, bool create)
213 {
214         DepsgraphStatsID *id_stats = (DepsgraphStatsID *)BLI_ghash_lookup(stats->id_stats, id);
215
216         if (!id_stats && create) {
217                 id_stats = (DepsgraphStatsID *)MEM_callocN(sizeof(DepsgraphStatsID),
218                                                            "Depsgraph ID Stats");
219                 id_stats->id = id;
220
221                 BLI_ghash_insert(stats->id_stats, id, id_stats);
222         }
223
224         return id_stats;
225 }
226
227 DepsgraphStatsComponent *DepsgraphDebug::get_component_stats(
228         DepsgraphStatsID *id_stats,
229         const char *name,
230         bool create)
231 {
232         DepsgraphStatsComponent *comp_stats;
233         for (comp_stats = (DepsgraphStatsComponent *)id_stats->components.first;
234              comp_stats != NULL;
235              comp_stats = comp_stats->next)
236         {
237                 if (STREQ(comp_stats->name, name)) {
238                         break;
239                 }
240         }
241         if (!comp_stats && create) {
242                 comp_stats = (DepsgraphStatsComponent *)MEM_callocN(sizeof(DepsgraphStatsComponent),
243                                                                     "Depsgraph Component Stats");
244                 BLI_strncpy(comp_stats->name, name, sizeof(comp_stats->name));
245                 BLI_addtail(&id_stats->components, comp_stats);
246         }
247         return comp_stats;
248 }
249
250 }  // namespace DEG