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