Blender  V2.93
deg_builder_relations_drivers.cc
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2013 Blender Foundation.
17  * All rights reserved.
18  */
19 
27 
28 #include <cstring>
29 
30 #include "DNA_anim_types.h"
31 
32 #include "BKE_anim_data.h"
33 
36 #include "intern/node/deg_node.h"
37 
38 namespace blender::deg {
39 
41  : id_ptr_(id_ptr),
42  fcu_(fcu),
43  driver_relations_needed_(false),
44  pointer_rna_(),
45  property_rna_(nullptr),
46  is_array_(false)
47 {
48  driver_relations_needed_ = determine_relations_needed();
49  split_rna_path();
50 }
51 
52 bool DriverDescriptor::determine_relations_needed()
53 {
54  if (fcu_->array_index > 0) {
55  /* Drivers on array elements always need relations. */
56  is_array_ = true;
57  return true;
58  }
59 
60  if (!resolve_rna()) {
61  /* Properties that don't exist can't cause threading issues either. */
62  return false;
63  }
64 
65  if (RNA_property_array_check(property_rna_)) {
66  /* Drivers on array elements always need relations. */
67  is_array_ = true;
68  return true;
69  }
70 
71  /* Drivers on Booleans and Enums (when used as bitflags) can write to the same memory location,
72  * so they need relations between each other. */
73  return ELEM(RNA_property_type(property_rna_), PROP_BOOLEAN, PROP_ENUM);
74 }
75 
77 {
78  return driver_relations_needed_;
79 }
80 
82 {
83  return is_array_;
84 }
85 
86 /* Assumes that 'other' comes from the same RNA group, that is, has the same RNA path prefix. */
88 {
89  if (!is_array_ || !other.is_array_) {
90  return false;
91  }
92  return rna_suffix == other.rna_suffix;
93 }
94 
96 {
97  return OperationKey(id_ptr_->owner_id,
100  fcu_->rna_path,
101  fcu_->array_index);
102 }
103 
104 void DriverDescriptor::split_rna_path()
105 {
106  const char *last_dot = strrchr(fcu_->rna_path, '.');
107  if (last_dot == nullptr || last_dot[1] == '\0') {
108  rna_prefix = StringRef();
109  rna_suffix = StringRef(fcu_->rna_path);
110  return;
111  }
112 
113  rna_prefix = StringRef(fcu_->rna_path, last_dot);
114  rna_suffix = StringRef(last_dot + 1);
115 }
116 
117 bool DriverDescriptor::resolve_rna()
118 {
119  return RNA_path_resolve_property(id_ptr_, fcu_->rna_path, &pointer_rna_, &property_rna_);
120 }
121 
122 static bool is_reachable(const Node *const from, const Node *const to)
123 {
124  if (from == to) {
125  return true;
126  }
127 
128  // Perform a graph walk from 'to' towards its incoming connections.
129  // Walking from 'from' towards its outgoing connections is 10x slower on the Spring rig.
130  deque<const Node *> queue;
131  Set<const Node *> seen;
132  queue.push_back(to);
133  while (!queue.empty()) {
134  // Visit the next node to inspect.
135  const Node *visit = queue.back();
136  queue.pop_back();
137 
138  if (visit == from) {
139  return true;
140  }
141 
142  // Queue all incoming relations that we haven't seen before.
143  for (Relation *relation : visit->inlinks) {
144  const Node *prev_node = relation->from;
145  if (seen.add(prev_node)) {
146  queue.push_back(prev_node);
147  }
148  }
149  }
150  return false;
151 }
152 
153 /* **** DepsgraphRelationBuilder functions **** */
154 
156 {
157  for (IDNode *id_node : graph_->id_nodes) {
159  }
160 }
161 
163 {
164  /* Add relations between drivers that write to the same datablock.
165  *
166  * This prevents threading issues when two separate RNA properties write to
167  * the same memory address. For example:
168  * - Drivers on individual array elements, as the animation system will write
169  * the whole array back to RNA even when changing individual array value.
170  * - Drivers on RNA properties that map to a single bit flag. Changing the RNA
171  * value will write the entire int containing the bit, in a non-thread-safe
172  * way.
173  */
174  ID *id_orig = id_node->id_orig;
175  AnimData *adt = BKE_animdata_from_id(id_orig);
176  if (adt == nullptr) {
177  return;
178  }
179 
180  // Mapping from RNA prefix -> set of driver descriptors:
182 
183  PointerRNA id_ptr;
184  RNA_id_pointer_create(id_orig, &id_ptr);
185 
186  LISTBASE_FOREACH (FCurve *, fcu, &adt->drivers) {
187  if (fcu->rna_path == nullptr) {
188  continue;
189  }
190 
191  DriverDescriptor driver_desc(&id_ptr, fcu);
192  if (!driver_desc.driver_relations_needed()) {
193  continue;
194  }
195 
196  driver_groups.lookup_or_add_default_as(driver_desc.rna_prefix).append(driver_desc);
197  }
198 
199  for (Span<DriverDescriptor> prefix_group : driver_groups.values()) {
200  // For each node in the driver group, try to connect it to another node
201  // in the same group without creating any cycles.
202  int num_drivers = prefix_group.size();
203  if (num_drivers < 2) {
204  // A relation requires two drivers.
205  continue;
206  }
207  for (int from_index = 0; from_index < num_drivers; ++from_index) {
208  const DriverDescriptor &driver_from = prefix_group[from_index];
209  Node *op_from = get_node(driver_from.depsgraph_key());
210 
211  // Start by trying the next node in the group.
212  for (int to_offset = 1; to_offset < num_drivers; ++to_offset) {
213  const int to_index = (from_index + to_offset) % num_drivers;
214  const DriverDescriptor &driver_to = prefix_group[to_index];
215  Node *op_to = get_node(driver_to.depsgraph_key());
216 
217  // Duplicate drivers can exist (see T78615), but cannot be distinguished by OperationKey
218  // and thus have the same depsgraph node. Relations between those drivers should not be
219  // created. This not something that is expected to happen (both the UI and the Python API
220  // prevent duplicate drivers), it did happen in a file and it is easy to deal with here.
221  if (op_from == op_to) {
222  continue;
223  }
224 
225  if (from_index < to_index && driver_from.is_same_array_as(driver_to)) {
226  // This is for adding a relation like `color[0]` -> `color[1]`.
227  // When the search for another driver wraps around, we cannot blindly add relations any
228  // more.
229  }
230  else {
231  // Investigate whether this relation would create a dependency cycle.
232  // Example graph:
233  // A -> B -> C
234  // and investigating a potential connection C->A. Because A->C is an
235  // existing transitive connection, adding C->A would create a cycle.
236  if (is_reachable(op_to, op_from)) {
237  continue;
238  }
239 
240  // No need to directly connect this node if there is already a transitive connection.
241  if (is_reachable(op_from, op_to)) {
242  break;
243  }
244  }
245 
247  op_from->get_exit_operation(), op_to->get_entry_operation(), "Driver Serialization");
248  break;
249  }
250  }
251  }
252 }
253 
254 } // namespace blender::deg
struct AnimData * BKE_animdata_from_id(struct ID *id)
Definition: anim_data.c:96
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:172
#define ELEM(...)
@ PROP_BOOLEAN
Definition: RNA_types.h:73
@ PROP_ENUM
Definition: RNA_types.h:77
Value & lookup_or_add_default_as(ForwardKey &&key)
Definition: BLI_map.hh:602
ValueIterator values() const
Definition: BLI_map.hh:806
bool add(const Key &key)
Definition: BLI_set.hh:267
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
ThreadQueue * queue
all scheduled work for the cpu
static bool is_reachable(const Node *const from, const Node *const to)
bool RNA_property_array_check(PropertyRNA *prop)
Definition: rna_access.c:1223
void RNA_id_pointer_create(ID *id, PointerRNA *r_ptr)
Definition: rna_access.c:122
PropertyType RNA_property_type(PropertyRNA *prop)
Definition: rna_access.c:1155
bool RNA_path_resolve_property(PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop)
Definition: rna_access.c:5434
ListBase drivers
char * rna_path
int array_index
Definition: DNA_ID.h:273
struct ID * owner_id
Definition: RNA_types.h:50
IDDepsNodes id_nodes
Definition: depsgraph.h:103
Relations inlinks
Definition: deg_node.h:175
virtual OperationNode * get_exit_operation()
Definition: deg_node.h:203
virtual OperationNode * get_entry_operation()
Definition: deg_node.h:199