Blender  V3.3
deg_builder_relations_drivers.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2013 Blender Foundation. All rights reserved. */
3 
11 
12 #include <cstring>
13 
14 #include "DNA_anim_types.h"
15 
16 #include "BKE_anim_data.h"
17 
20 #include "intern/node/deg_node.h"
21 
22 namespace blender::deg {
23 
25  : id_ptr_(id_ptr),
26  fcu_(fcu),
27  driver_relations_needed_(false),
28  pointer_rna_(),
29  property_rna_(nullptr),
30  is_array_(false)
31 {
32  driver_relations_needed_ = determine_relations_needed();
33  split_rna_path();
34 }
35 
36 bool DriverDescriptor::determine_relations_needed()
37 {
38  if (fcu_->array_index > 0) {
39  /* Drivers on array elements always need relations. */
40  is_array_ = true;
41  return true;
42  }
43 
44  if (!resolve_rna()) {
45  /* Properties that don't exist can't cause threading issues either. */
46  return false;
47  }
48 
49  if (RNA_property_array_check(property_rna_)) {
50  /* Drivers on array elements always need relations. */
51  is_array_ = true;
52  return true;
53  }
54 
55  /* Drivers on Booleans and Enums (when used as bitflags) can write to the same memory location,
56  * so they need relations between each other. */
57  return ELEM(RNA_property_type(property_rna_), PROP_BOOLEAN, PROP_ENUM);
58 }
59 
61 {
62  return driver_relations_needed_;
63 }
64 
66 {
67  return is_array_;
68 }
69 
71 {
72  if (!is_array_ || !other.is_array_) {
73  return false;
74  }
75  return rna_suffix == other.rna_suffix;
76 }
77 
79 {
80  return OperationKey(id_ptr_->owner_id,
83  fcu_->rna_path,
84  fcu_->array_index);
85 }
86 
87 void DriverDescriptor::split_rna_path()
88 {
89  const char *last_dot = strrchr(fcu_->rna_path, '.');
90  if (last_dot == nullptr || last_dot[1] == '\0') {
92  rna_suffix = StringRef(fcu_->rna_path);
93  return;
94  }
95 
96  rna_prefix = StringRef(fcu_->rna_path, last_dot);
97  rna_suffix = StringRef(last_dot + 1);
98 }
99 
100 bool DriverDescriptor::resolve_rna()
101 {
102  return RNA_path_resolve_property(id_ptr_, fcu_->rna_path, &pointer_rna_, &property_rna_);
103 }
104 
105 static bool is_reachable(const Node *const from, const Node *const to)
106 {
107  if (from == to) {
108  return true;
109  }
110 
111  /* Perform a graph walk from 'to' towards its incoming connections.
112  * Walking from 'from' towards its outgoing connections is 10x slower on the Spring rig. */
113  deque<const Node *> queue;
114  Set<const Node *> seen;
115  queue.push_back(to);
116  while (!queue.empty()) {
117  /* Visit the next node to inspect. */
118  const Node *visit = queue.back();
119  queue.pop_back();
120 
121  if (visit == from) {
122  return true;
123  }
124 
125  /* Queue all incoming relations that we haven't seen before. */
126  for (Relation *relation : visit->inlinks) {
127  const Node *prev_node = relation->from;
128  if (seen.add(prev_node)) {
129  queue.push_back(prev_node);
130  }
131  }
132  }
133  return false;
134 }
135 
136 /* **** DepsgraphRelationBuilder functions **** */
137 
139 {
140  for (IDNode *id_node : graph_->id_nodes) {
142  }
143 }
144 
146 {
147  /* Add relations between drivers that write to the same datablock.
148  *
149  * This prevents threading issues when two separate RNA properties write to
150  * the same memory address. For example:
151  * - Drivers on individual array elements, as the animation system will write
152  * the whole array back to RNA even when changing individual array value.
153  * - Drivers on RNA properties that map to a single bit flag. Changing the RNA
154  * value will write the entire int containing the bit, in a non-thread-safe
155  * way.
156  */
157  ID *id_orig = id_node->id_orig;
158  AnimData *adt = BKE_animdata_from_id(id_orig);
159  if (adt == nullptr) {
160  return;
161  }
162 
163  /* Mapping from RNA prefix -> set of driver descriptors: */
165 
166  PointerRNA id_ptr;
167  RNA_id_pointer_create(id_orig, &id_ptr);
168 
169  LISTBASE_FOREACH (FCurve *, fcu, &adt->drivers) {
170  if (fcu->rna_path == nullptr) {
171  continue;
172  }
173 
174  DriverDescriptor driver_desc(&id_ptr, fcu);
175  if (!driver_desc.driver_relations_needed()) {
176  continue;
177  }
178 
179  driver_groups.lookup_or_add_default_as(driver_desc.rna_prefix).append(driver_desc);
180  }
181 
182  for (Span<DriverDescriptor> prefix_group : driver_groups.values()) {
183  /* For each node in the driver group, try to connect it to another node
184  * in the same group without creating any cycles. */
185  int num_drivers = prefix_group.size();
186  if (num_drivers < 2) {
187  /* A relation requires two drivers. */
188  continue;
189  }
190  for (int from_index = 0; from_index < num_drivers; ++from_index) {
191  const DriverDescriptor &driver_from = prefix_group[from_index];
192  Node *op_from = get_node(driver_from.depsgraph_key());
193 
194  /* Start by trying the next node in the group. */
195  for (int to_offset = 1; to_offset < num_drivers; ++to_offset) {
196  const int to_index = (from_index + to_offset) % num_drivers;
197  const DriverDescriptor &driver_to = prefix_group[to_index];
198  Node *op_to = get_node(driver_to.depsgraph_key());
199 
200  /* Duplicate drivers can exist (see T78615), but cannot be distinguished by OperationKey
201  * and thus have the same depsgraph node. Relations between those drivers should not be
202  * created. This not something that is expected to happen (both the UI and the Python API
203  * prevent duplicate drivers), it did happen in a file and it is easy to deal with here. */
204  if (op_from == op_to) {
205  continue;
206  }
207 
208  if (from_index < to_index && driver_from.is_same_array_as(driver_to)) {
209  /* This is for adding a relation like `color[0]` -> `color[1]`.
210  * When the search for another driver wraps around,
211  * we cannot blindly add relations any more. */
212  }
213  else {
214  /* Investigate whether this relation would create a dependency cycle.
215  * Example graph:
216  * A -> B -> C
217  * and investigating a potential connection C->A. Because A->C is an
218  * existing transitive connection, adding C->A would create a cycle. */
219  if (is_reachable(op_to, op_from)) {
220  continue;
221  }
222 
223  /* No need to directly connect this node if there is already a transitive connection. */
224  if (is_reachable(op_from, op_to)) {
225  break;
226  }
227  }
228 
230  op_from->get_exit_operation(), op_to->get_entry_operation(), "Driver Serialization");
231  break;
232  }
233  }
234  }
235 }
236 
237 } // namespace blender::deg
struct AnimData * BKE_animdata_from_id(const struct ID *id)
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:336
#define ELEM(...)
@ PROP_BOOLEAN
Definition: RNA_types.h:59
@ PROP_ENUM
Definition: RNA_types.h:63
Value & lookup_or_add_default_as(ForwardKey &&key)
Definition: BLI_map.hh:588
ValueIterator values() const
Definition: BLI_map.hh:840
bool add(const Key &key)
Definition: BLI_set.hh:253
TimeSourceNode * get_node(const TimeSourceKey &key) const
Relation * add_operation_relation(OperationNode *node_from, OperationNode *node_to, const char *description, int flags=0)
DriverDescriptor(PointerRNA *id_ptr, FCurve *fcu)
bool is_same_array_as(const DriverDescriptor &other) const
StackEntry * from
const IDNode * id_node
SyclQueue * queue
static bool is_reachable(const Node *const from, const Node *const to)
bool RNA_property_array_check(PropertyRNA *prop)
Definition: rna_access.c:1080
void RNA_id_pointer_create(ID *id, PointerRNA *r_ptr)
Definition: rna_access.c:112
PropertyType RNA_property_type(PropertyRNA *prop)
Definition: rna_access.c:1010
bool RNA_path_resolve_property(const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop)
Definition: rna_path.cc:531
ListBase drivers
char * rna_path
int array_index
Definition: DNA_ID.h:368
struct ID * owner_id
Definition: RNA_types.h:36
IDDepsNodes id_nodes
Definition: depsgraph.h:86
Relations inlinks
Definition: deg_node.h:173
virtual OperationNode * get_exit_operation()
Definition: deg_node.h:202
virtual OperationNode * get_entry_operation()
Definition: deg_node.h:198