[Bf-blender-cvs] [c006ba83e0b] master: Fix: execution graph for geometry nodes contained cycles leading to crash
Jacques Lucke
noreply at git.blender.org
Fri Jan 20 14:38:24 CET 2023
Commit: c006ba83e0b296983b53d924cfbd0c69aad12de6
Author: Jacques Lucke
Date: Fri Jan 20 14:38:09 2023 +0100
Branches: master
https://developer.blender.org/rBc006ba83e0b296983b53d924cfbd0c69aad12de6
Fix: execution graph for geometry nodes contained cycles leading to crash
The `fix_link_cycles` function added in rB2ffd08e95249df2a068dd did not
handle the case correctly when there are multiple cycles going through
the same socket.
===================================================================
M source/blender/nodes/intern/geometry_nodes_lazy_function.cc
===================================================================
diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc
index 54f8c3c912d..64551249e29 100644
--- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc
+++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc
@@ -2571,28 +2571,31 @@ struct GeometryNodesLazyFunctionGraphBuilder {
Array<SocketState> socket_states(sockets_num);
- Stack<lf::Socket *> lf_sockets_to_check;
+ Vector<lf::Socket *> lf_sockets_to_check;
for (lf::Node *lf_node : lf_graph_->nodes()) {
if (lf_node->is_function()) {
for (lf::OutputSocket *lf_socket : lf_node->outputs()) {
if (lf_socket->targets().is_empty()) {
- lf_sockets_to_check.push(lf_socket);
+ lf_sockets_to_check.append(lf_socket);
}
}
}
if (lf_node->outputs().is_empty()) {
for (lf::InputSocket *lf_socket : lf_node->inputs()) {
- lf_sockets_to_check.push(lf_socket);
+ lf_sockets_to_check.append(lf_socket);
}
}
}
Vector<lf::Socket *> lf_socket_stack;
while (!lf_sockets_to_check.is_empty()) {
- lf::Socket *lf_inout_socket = lf_sockets_to_check.peek();
+ lf::Socket *lf_inout_socket = lf_sockets_to_check.last();
lf::Node &lf_node = lf_inout_socket->node();
SocketState &state = socket_states[lf_inout_socket->index_in_graph()];
- lf_socket_stack.append(lf_inout_socket);
- state.in_stack = true;
+
+ if (!state.in_stack) {
+ lf_socket_stack.append(lf_inout_socket);
+ state.in_stack = true;
+ }
Vector<lf::Socket *, 16> lf_origin_sockets;
if (lf_inout_socket->is_input()) {
@@ -2616,10 +2619,24 @@ struct GeometryNodesLazyFunctionGraphBuilder {
}
bool pushed_socket = false;
+ bool detected_cycle = false;
for (lf::Socket *lf_origin_socket : lf_origin_sockets) {
if (socket_states[lf_origin_socket->index_in_graph()].in_stack) {
+ /* A cycle has been detected. The cycle is broken by removing a link and replacing it
+ * with a constant "true" input. This can only affect inputs which determine whether a
+ * specific value is used. Therefore, setting it to a constant true can result in more
+ * computation later, but does not change correctness.
+ *
+ * After the cycle is broken, the cycle-detection is "rolled back" to the socket where
+ * the first socket of the cycle was found. This is necessary in case another cycle goes
+ * through this socket. */
+
+ detected_cycle = true;
+ const int index_in_socket_stack = lf_socket_stack.first_index_of(lf_origin_socket);
+ const int index_in_sockets_to_check = lf_sockets_to_check.first_index_of(
+ lf_origin_socket);
const Span<lf::Socket *> cycle = lf_socket_stack.as_span().drop_front(
- lf_socket_stack.first_index_of(lf_origin_socket));
+ index_in_socket_stack);
bool broke_cycle = false;
for (lf::Socket *lf_cycle_socket : cycle) {
@@ -2631,23 +2648,35 @@ struct GeometryNodesLazyFunctionGraphBuilder {
lf_cycle_input_socket.set_default_value(&static_true);
broke_cycle = true;
}
+ /* This is actually removed from the stack when it is resized below. */
+ SocketState &lf_cycle_socket_state = socket_states[lf_cycle_socket->index_in_graph()];
+ lf_cycle_socket_state.in_stack = false;
}
if (!broke_cycle) {
BLI_assert_unreachable();
}
+ /* Roll back algorithm by removing the sockets that corresponded to the cycle from the
+ * stacks. */
+ lf_socket_stack.resize(index_in_socket_stack);
+ /* The +1 is there so that the socket itself is not removed. */
+ lf_sockets_to_check.resize(index_in_sockets_to_check + 1);
+ break;
}
else if (!socket_states[lf_origin_socket->index_in_graph()].done) {
- lf_sockets_to_check.push(lf_origin_socket);
+ lf_sockets_to_check.append(lf_origin_socket);
pushed_socket = true;
}
}
+ if (detected_cycle) {
+ continue;
+ }
if (pushed_socket) {
continue;
}
state.done = true;
state.in_stack = false;
- lf_sockets_to_check.pop();
+ lf_sockets_to_check.pop_last();
lf_socket_stack.pop_last();
}
}
More information about the Bf-blender-cvs
mailing list