Depsgraph: Cleanup, de-duplicate couple of utility functions
[blender.git] / source / blender / depsgraph / intern / eval / deg_eval_flush.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) 2013 Blender Foundation.
19  * All rights reserved.
20  *
21  * Original Author: Joshua Leung
22  * Contributor(s): None Yet
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/depsgraph/intern/eval/deg_eval_flush.cc
28  *  \ingroup depsgraph
29  *
30  * Core routines for how the Depsgraph works.
31  */
32
33 #include "intern/eval/deg_eval_flush.h"
34
35 // TODO(sergey): Use some sort of wrapper.
36 #include <deque>
37
38 #include "BLI_utildefines.h"
39 #include "BLI_task.h"
40 #include "BLI_ghash.h"
41
42 extern "C" {
43 #include "DNA_object_types.h"
44 } /* extern "C" */
45
46 #include "DEG_depsgraph.h"
47
48 #include "intern/nodes/deg_node.h"
49 #include "intern/nodes/deg_node_component.h"
50 #include "intern/nodes/deg_node_operation.h"
51
52 #include "intern/depsgraph_intern.h"
53 #include "util/deg_util_foreach.h"
54
55 namespace DEG {
56
57 typedef std::deque<OperationDepsNode *> FlushQueue;
58
59 static void flush_init_func(void *data_v, int i)
60 {
61         /* ID node's done flag is used to avoid multiple editors update
62          * for the same ID.
63          */
64         Depsgraph *graph = (Depsgraph *)data_v;
65         OperationDepsNode *node = graph->operations[i];
66         ComponentDepsNode *comp_node = node->owner;
67         IDDepsNode *id_node = comp_node->owner;
68         id_node->done = 0;
69         comp_node->done = 0;
70         node->scheduled = false;
71 }
72
73 /* Flush updates from tagged nodes outwards until all affected nodes
74  * are tagged.
75  */
76 void deg_graph_flush_updates(Main *bmain, Depsgraph *graph)
77 {
78         /* Sanity check. */
79         if (graph == NULL) {
80                 return;
81         }
82
83         /* Nothing to update, early out. */
84         if (BLI_gset_size(graph->entry_tags) == 0) {
85                 return;
86         }
87
88         /* TODO(sergey): With a bit of flag magic we can get rid of this
89          * extra loop.
90          */
91         const int num_operations = graph->operations.size();
92         const bool do_threads = num_operations > 256;
93         BLI_task_parallel_range(0,
94                                 num_operations,
95                                 graph,
96                                 flush_init_func,
97                                 do_threads);
98
99         FlushQueue queue;
100         /* Starting from the tagged "entry" nodes, flush outwards... */
101         /* NOTE: Also need to ensure that for each of these, there is a path back to
102          *       root, or else they won't be done.
103          * NOTE: Count how many nodes we need to handle - entry nodes may be
104          *       component nodes which don't count for this purpose!
105          */
106         GSET_FOREACH_BEGIN(OperationDepsNode *, op_node, graph->entry_tags)
107         {
108                 if ((op_node->flag & DEPSOP_FLAG_SKIP_FLUSH) == 0) {
109                         queue.push_back(op_node);
110                         op_node->scheduled = true;
111                 }
112         }
113         GSET_FOREACH_END();
114
115         int num_flushed_objects = 0;
116         while (!queue.empty()) {
117                 OperationDepsNode *node = queue.front();
118                 queue.pop_front();
119
120                 for (;;) {
121                         node->flag |= DEPSOP_FLAG_NEEDS_UPDATE;
122
123                         ComponentDepsNode *comp_node = node->owner;
124                         IDDepsNode *id_node = comp_node->owner;
125
126                         /* TODO(sergey): Do we need to pass original or evaluated ID here? */
127                         ID *id = id_node->id_orig;
128                         if (id_node->done == 0) {
129                                 deg_editors_id_update(bmain, id);
130                                 lib_id_recalc_tag(bmain, id);
131                                 /* TODO(sergey): For until we've got proper data nodes in the graph. */
132                                 lib_id_recalc_data_tag(bmain, id);
133
134 #ifdef WITH_COPY_ON_WRITE
135                                 /* Currently this is needed to get ob->mesh to be replaced with
136                                  * original mesh (rather than being evaluated_mesh).
137                                  *
138                                  * TODO(sergey): This is something we need to avoid.
139                                  */
140                                 ComponentDepsNode *cow_comp =
141                                         id_node->find_component(DEG_NODE_TYPE_COPY_ON_WRITE);
142                                 cow_comp->tag_update(graph);
143 #endif
144                         }
145
146                         if (comp_node->done == 0) {
147                                 Object *object = NULL;
148                                 if (GS(id->name) == ID_OB) {
149                                         object = (Object *)id;
150                                         if (id_node->done == 0) {
151                                                 ++num_flushed_objects;
152                                         }
153                                 }
154                                 foreach (OperationDepsNode *op, comp_node->operations) {
155                                         op->flag |= DEPSOP_FLAG_NEEDS_UPDATE;
156                                 }
157                                 if (object != NULL) {
158                                         /* This code is used to preserve those areas which does
159                                          * direct object update,
160                                          *
161                                          * Plus it ensures visibility changes and relations and
162                                          * layers visibility update has proper flags to work with.
163                                          */
164                                         switch (comp_node->type) {
165                                                 case DEG_NODE_TYPE_UNDEFINED:
166                                                 case DEG_NODE_TYPE_OPERATION:
167                                                 case DEG_NODE_TYPE_TIMESOURCE:
168                                                 case DEG_NODE_TYPE_ID_REF:
169                                                 case DEG_NODE_TYPE_PARAMETERS:
170                                                 case DEG_NODE_TYPE_SEQUENCER:
171                                                 case DEG_NODE_TYPE_LAYER_COLLECTIONS:
172                                                 case DEG_NODE_TYPE_COPY_ON_WRITE:
173                                                         /* Ignore, does not translate to object component. */
174                                                         break;
175                                                 case DEG_NODE_TYPE_ANIMATION:
176                                                         object->recalc |= OB_RECALC_TIME;
177                                                         break;
178                                                 case DEG_NODE_TYPE_TRANSFORM:
179                                                         object->recalc |= OB_RECALC_OB;
180                                                         break;
181                                                 case DEG_NODE_TYPE_GEOMETRY:
182                                                 case DEG_NODE_TYPE_EVAL_POSE:
183                                                 case DEG_NODE_TYPE_BONE:
184                                                 case DEG_NODE_TYPE_EVAL_PARTICLES:
185                                                 case DEG_NODE_TYPE_SHADING:
186                                                 case DEG_NODE_TYPE_CACHE:
187                                                 case DEG_NODE_TYPE_PROXY:
188                                                         object->recalc |= OB_RECALC_DATA;
189                                                         break;
190                                         }
191
192                                         /* TODO : replace with more granular flags */
193                                         object->deg_update_flag |= DEG_RUNTIME_DATA_UPDATE;
194                                 }
195                         }
196
197                         id_node->done = 1;
198                         comp_node->done = 1;
199
200                         /* Flush to nodes along links... */
201                         /* TODO(sergey): This is mainly giving speedup due ot less queue pushes, which
202                          * reduces number of memory allocations.
203                          *
204                          * We should try solve the allocation issue instead of doing crazy things here.
205                          */
206                         if (node->outlinks.size() == 1) {
207                                 OperationDepsNode *to_node = (OperationDepsNode *)node->outlinks[0]->to;
208                                 if (to_node->scheduled == false) {
209                                         to_node->scheduled = true;
210                                         node = to_node;
211                                 }
212                                 else {
213                                         break;
214                                 }
215                         }
216                         else {
217                                 foreach (DepsRelation *rel, node->outlinks) {
218                                         OperationDepsNode *to_node = (OperationDepsNode *)rel->to;
219                                         if (to_node->scheduled == false) {
220                                                 queue.push_front(to_node);
221                                                 to_node->scheduled = true;
222                                         }
223                                 }
224                                 break;
225                         }
226                 }
227         }
228         DEG_DEBUG_PRINTF("Update flushed to %d objects\n", num_flushed_objects);
229 }
230
231 static void graph_clear_func(void *data_v, int i)
232 {
233         Depsgraph *graph = (Depsgraph *)data_v;
234         OperationDepsNode *node = graph->operations[i];
235         /* Clear node's "pending update" settings. */
236         node->flag &= ~(DEPSOP_FLAG_DIRECTLY_MODIFIED | DEPSOP_FLAG_NEEDS_UPDATE);
237 }
238
239 /* Clear tags from all operation nodes. */
240 void deg_graph_clear_tags(Depsgraph *graph)
241 {
242         /* Go over all operation nodes, clearing tags. */
243         const int num_operations = graph->operations.size();
244         const bool do_threads = num_operations > 256;
245         BLI_task_parallel_range(0, num_operations, graph, graph_clear_func, do_threads);
246         /* Clear any entry tags which haven't been flushed. */
247         BLI_gset_clear(graph->entry_tags, NULL);
248 }
249
250 }  // namespace DEG