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