Blender V4.3
deg_eval_flush.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2013 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
12
13#include <cmath>
14
15#include "BLI_listbase.h"
16#include "BLI_math_vector.h"
17#include "BLI_task.h"
18#include "BLI_utildefines.h"
19
20#include "BKE_key.hh"
21#include "BKE_object.hh"
22#include "BKE_scene.hh"
23
24#include "DNA_key_types.h"
25#include "DNA_object_types.h"
26#include "DNA_scene_types.h"
27
28#include "DRW_engine.hh"
29
30#include "DEG_depsgraph.hh"
31
33#include "intern/depsgraph.hh"
43
45
46/* Invalidate data-block data when update is flushed on it.
47 *
48 * The idea of this is to help catching cases when area is accessing data which
49 * is not yet evaluated, which could happen due to missing relations. The issue
50 * is that usually that data will be kept from previous frame, and it looks to
51 * be plausible.
52 *
53 * This ensures that data does not look plausible, making it much easier to
54 * catch usage of invalid state. */
55#undef INVALIDATE_ON_FLUSH
56
57namespace blender::deg {
58
59enum {
62};
63
64enum {
68};
69
70using FlushQueue = deque<OperationNode *>;
71
72namespace {
73
74void flush_init_id_node_func(void *__restrict data_v,
75 const int i,
76 const TaskParallelTLS *__restrict /*tls*/)
77{
78 Depsgraph *graph = (Depsgraph *)data_v;
79 IDNode *id_node = graph->id_nodes[i];
81 for (ComponentNode *comp_node : id_node->components.values()) {
82 comp_node->custom_flags = COMPONENT_STATE_NONE;
83 }
84}
85
86inline void flush_prepare(Depsgraph *graph)
87{
88 for (OperationNode *node : graph->operations) {
89 node->scheduled = false;
90 }
91
92 {
93 const int num_id_nodes = graph->id_nodes.size();
94 TaskParallelSettings settings;
96 settings.min_iter_per_thread = 1024;
97 BLI_task_parallel_range(0, num_id_nodes, graph, flush_init_id_node_func, &settings);
98 }
99}
100
101inline void flush_schedule_entrypoints(Depsgraph *graph, FlushQueue *queue)
102{
103 for (OperationNode *op_node : graph->entry_tags) {
104 queue->push_back(op_node);
105 op_node->scheduled = true;
106 DEG_DEBUG_PRINTF((::Depsgraph *)graph,
107 EVAL,
108 "Operation is entry point for update: %s\n",
109 op_node->identifier().c_str());
110 }
111}
112
113inline void flush_handle_id_node(IDNode *id_node)
114{
116}
117
118/* TODO(sergey): We can reduce number of arguments here. */
119inline void flush_handle_component_node(IDNode *id_node,
120 ComponentNode *comp_node,
121 FlushQueue *queue)
122{
123 /* We only handle component once. */
124 if (comp_node->custom_flags == COMPONENT_STATE_DONE) {
125 return;
126 }
128 /* Tag all required operations in component for update, unless this is a
129 * special component where we don't want all operations to be tagged.
130 *
131 * TODO(sergey): Make this a more generic solution. */
133 const bool is_geometry_component = comp_node->type == NodeType::GEOMETRY;
134 for (OperationNode *op : comp_node->operations) {
135 /* Special case for the visibility operation in the geometry component.
136 *
137 * This operation is a part of the geometry component so that manual tag for geometry recalc
138 * ensures that the visibility is re-evaluated. This operation is not to be re-evaluated when
139 * an update is flushed to the geometry component via a time dependency or a driver targeting
140 * a modifier. Skipping update in this case avoids CPU time unnecessarily spent looping over
141 * modifiers and looking up operations by name in the visibility evaluation function. */
142 if (is_geometry_component && op->opcode == OperationCode::VISIBILITY) {
143 continue;
144 }
145 op->flag |= DEPSOP_FLAG_NEEDS_UPDATE;
146 }
147 }
148 /* when some target changes bone, we might need to re-run the
149 * whole IK solver, otherwise result might be unpredictable. */
150 if (comp_node->type == NodeType::BONE) {
152 BLI_assert(pose_comp != nullptr);
153 if (pose_comp->custom_flags == COMPONENT_STATE_NONE) {
154 queue->push_front(pose_comp->get_entry_operation());
156 }
157 }
158}
159
160/* Schedule children of the given operation node for traversal.
161 *
162 * One of the children will by-pass the queue and will be returned as a function
163 * return value, so it can start being handled right away, without building too
164 * much of a queue.
165 */
166inline OperationNode *flush_schedule_children(OperationNode *op_node, FlushQueue *queue)
167{
168 if (op_node->flag & DEPSOP_FLAG_USER_MODIFIED) {
169 IDNode *id_node = op_node->owner->owner;
171 }
172
173 OperationNode *result = nullptr;
174 for (Relation *rel : op_node->outlinks) {
175 /* Flush is forbidden, completely. */
176 if (rel->flag & RELATION_FLAG_NO_FLUSH) {
177 continue;
178 }
179 /* Relation only allows flushes on user changes, but the node was not
180 * affected by user. */
181 if ((rel->flag & RELATION_FLAG_FLUSH_USER_EDIT_ONLY) &&
182 (op_node->flag & DEPSOP_FLAG_USER_MODIFIED) == 0)
183 {
184 continue;
185 }
186 OperationNode *to_node = (OperationNode *)rel->to;
187 /* Always flush flushable flags, so children always know what happened
188 * to their parents. */
189 to_node->flag |= (op_node->flag & DEPSOP_FLAG_FLUSH);
190 /* Flush update over the relation, if it was not flushed yet. */
191 if (to_node->scheduled) {
192 continue;
193 }
194 if (result != nullptr) {
195 queue->push_front(to_node);
196 }
197 else {
198 result = to_node;
199 }
200 to_node->scheduled = true;
201 }
202 return result;
203}
204
205void flush_engine_data_update(ID *id)
206{
207 DrawDataList *draw_data_list = DRW_drawdatalist_from_id(id);
208 if (draw_data_list == nullptr) {
209 return;
210 }
211 LISTBASE_FOREACH (DrawData *, draw_data, draw_data_list) {
212 draw_data->recalc |= id->recalc;
213 }
214}
215
216/* NOTE: It will also accumulate flags from changed components. */
217void flush_editors_id_update(Depsgraph *graph, const DEGEditorUpdateContext *update_ctx)
218{
219 for (IDNode *id_node : graph->id_nodes) {
221 continue;
222 }
223 DEG_graph_id_type_tag(reinterpret_cast<::Depsgraph *>(graph), GS(id_node->id_orig->name));
224 /* TODO(sergey): Do we need to pass original or evaluated ID here? */
225 ID *id_orig = id_node->id_orig;
226 ID *id_cow = id_node->id_cow;
227 /* Gather recalc flags from all changed components. */
228 for (ComponentNode *comp_node : id_node->components.values()) {
229 if (comp_node->custom_flags != COMPONENT_STATE_DONE) {
230 continue;
231 }
232 DepsNodeFactory *factory = type_get_factory(comp_node->type);
233 BLI_assert(factory != nullptr);
234 id_cow->recalc |= factory->id_recalc_tag();
235 }
236 DEG_DEBUG_PRINTF((::Depsgraph *)graph,
237 EVAL,
238 "Accumulated recalc bits for %s: %u\n",
239 id_orig->name,
240 uint(id_cow->recalc));
241
242 /* Inform editors. Only if the data-block is being evaluated a second
243 * time, to distinguish between user edits and initial evaluation when
244 * the data-block becomes visible.
245 *
246 * TODO: image data-blocks do not use copy-on-eval, so might not be detected
247 * correctly. */
248 if (deg_eval_copy_is_expanded(id_cow)) {
249 if (graph->is_active && id_node->is_user_modified) {
250 deg_editors_id_update(update_ctx, id_orig);
251 }
252 /* Inform draw engines that something was changed. */
253 flush_engine_data_update(id_cow);
254 }
255 }
256}
257
258#ifdef INVALIDATE_ON_FLUSH
259void invalidate_tagged_evaluated_transform(ID *id)
260{
261 const ID_Type id_type = GS(id->name);
262 switch (id_type) {
263 case ID_OB: {
264 Object *object = (Object *)id;
265 copy_vn_fl((float *)object->object_to_world().ptr(), 16, NAN);
266 break;
267 }
268 default:
269 break;
270 }
271}
272
273void invalidate_tagged_evaluated_geometry(ID *id)
274{
275 const ID_Type id_type = GS(id->name);
276 switch (id_type) {
277 case ID_OB: {
278 Object *object = (Object *)id;
280 break;
281 }
282 default:
283 break;
284 }
285}
286#endif
287
288void invalidate_tagged_evaluated_data(Depsgraph *graph)
289{
290#ifdef INVALIDATE_ON_FLUSH
291 for (IDNode *id_node : graph->id_nodes) {
293 continue;
294 }
295 ID *id_cow = id_node->id_cow;
296 if (!deg_eval_copy_is_expanded(id_cow)) {
297 continue;
298 }
299 for (ComponentNode *comp_node : id_node->components.values()) {
300 if (comp_node->custom_flags != COMPONENT_STATE_DONE) {
301 continue;
302 }
303 switch (comp_node->type) {
305 invalidate_tagged_evaluated_transform(id_cow);
306 break;
308 invalidate_tagged_evaluated_geometry(id_cow);
309 break;
310 default:
311 break;
312 }
313 }
314 }
315#else
316 (void)graph;
317#endif
318}
319
320} // namespace
321
323{
324 /* Sanity checks. */
325 BLI_assert(graph != nullptr);
326 Main *bmain = graph->bmain;
327
328 graph->time_source->flush_update_tag(graph);
329
330 /* Nothing to update, early out. */
331 if (graph->entry_tags.is_empty()) {
332 return;
333 }
334 /* Reset all flags, get ready for the flush. */
335 flush_prepare(graph);
336 /* Starting from the tagged "entry" nodes, flush outwards. */
337 FlushQueue queue;
338 flush_schedule_entrypoints(graph, &queue);
339 /* Prepare update context for editors. */
340 DEGEditorUpdateContext update_ctx;
341 update_ctx.bmain = bmain;
342 update_ctx.depsgraph = (::Depsgraph *)graph;
343 update_ctx.scene = graph->scene;
344 update_ctx.view_layer = graph->view_layer;
345 /* Do actual flush. */
346 while (!queue.empty()) {
347 OperationNode *op_node = queue.front();
348 queue.pop_front();
349 while (op_node != nullptr) {
350 /* Tag operation as required for update. */
351 op_node->flag |= DEPSOP_FLAG_NEEDS_UPDATE;
352 /* Inform corresponding ID and component nodes about the change. */
353 ComponentNode *comp_node = op_node->owner;
354 IDNode *id_node = comp_node->owner;
355 flush_handle_id_node(id_node);
356 flush_handle_component_node(id_node, comp_node, &queue);
357 /* Flush to nodes along links. */
358 op_node = flush_schedule_children(op_node, &queue);
359 }
360 }
361 /* Inform editors about all changes. */
362 flush_editors_id_update(graph, &update_ctx);
363 /* Reset evaluation result tagged which is tagged for update to some state
364 * which is obvious to catch. */
365 invalidate_tagged_evaluated_data(graph);
366}
367
369{
370 /* Clear any entry tags which haven't been flushed. */
371 graph->entry_tags.clear();
372
373 graph->time_source->tagged_for_update = false;
374}
375
376} // namespace blender::deg
General operations, lookup, etc. for blender objects.
void BKE_object_free_derived_caches(Object *ob)
#define BLI_assert(a)
Definition BLI_assert.h:50
#define LISTBASE_FOREACH(type, var, list)
void copy_vn_fl(float *array_tar, int size, float val)
unsigned int uint
void BLI_task_parallel_range(int start, int stop, void *userdata, TaskParallelRangeFunc func, const TaskParallelSettings *settings)
Definition task_range.cc:99
BLI_INLINE void BLI_parallel_range_settings_defaults(TaskParallelSettings *settings)
Definition BLI_task.h:230
#define ELEM(...)
void DEG_graph_id_type_tag(Depsgraph *depsgraph, short id_type)
@ ID_RECALC_TRANSFORM
Definition DNA_ID.h:1021
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1041
ID_Type
@ ID_OB
Object is a sort of wrapper for general info.
DrawDataList * DRW_drawdatalist_from_id(ID *id)
#define DEG_DEBUG_PRINTF(depsgraph, type,...)
Definition deg_debug.h:45
const IDNode * id_node
#define GS(x)
Definition iris.cc:202
void deg_editors_id_update(const DEGEditorUpdateContext *update_ctx, ID *id)
deque< OperationNode * > FlushQueue
bool deg_eval_copy_is_expanded(const ID *id_cow)
void deg_graph_clear_tags(Depsgraph *graph)
DepsNodeFactory * type_get_factory(const NodeType type)
void deg_graph_flush_updates(Depsgraph *graph)
Definition DNA_ID.h:413
unsigned int recalc
Definition DNA_ID.h:437
char name[66]
Definition DNA_ID.h:425
virtual OperationNode * get_entry_operation() override
Vector< OperationNode * > operations
virtual int id_recalc_tag() const =0
Map< ComponentIDKey, ComponentNode * > components
ComponentNode * find_component(NodeType type, const char *name="") const
Relations outlinks
Definition deg_node.hh:181