Changeset View
Changeset View
Standalone View
Standalone View
source/blender/modifiers/intern/MOD_nodes_evaluator.cc
| Show First 20 Lines • Show All 120 Lines • ▼ Show 20 Lines | struct InputState { | ||||
| * the node is running correctly trigger the node to run again. Furthermore, it gives the node a | * the node is running correctly trigger the node to run again. Furthermore, it gives the node a | ||||
| * consistent view of which inputs are available that does not change unexpectedly. | * consistent view of which inputs are available that does not change unexpectedly. | ||||
| * | * | ||||
| * While the node is running, this can be checked without a lock, because no one is writing to | * While the node is running, this can be checked without a lock, because no one is writing to | ||||
| * it. If this is true, the value can be read without a lock as well, because the value is not | * it. If this is true, the value can be read without a lock as well, because the value is not | ||||
| * changed by others anymore. | * changed by others anymore. | ||||
| */ | */ | ||||
| bool was_ready_for_execution = false; | bool was_ready_for_execution = false; | ||||
| /** | |||||
| * True when this input has to be computed for logging/debugging purposes, regardless of whether | |||||
| * it is needed for some output. | |||||
| */ | |||||
| bool force_compute = false; | |||||
| }; | }; | ||||
| struct OutputState { | struct OutputState { | ||||
| /** | /** | ||||
| * If this output has been computed and forwarded already. If this is true, the value is not | * If this output has been computed and forwarded already. If this is true, the value is not | ||||
| * computed/forwarded again. | * computed/forwarded again. | ||||
| */ | */ | ||||
| bool has_been_computed = false; | bool has_been_computed = false; | ||||
| ▲ Show 20 Lines • Show All 351 Lines • ▼ Show 20 Lines | void create_states_for_reachable_nodes() | ||||
| * multiple threads. */ | * multiple threads. */ | ||||
| threading::parallel_for( | threading::parallel_for( | ||||
| IndexRange(node_states_.size()), 50, [&, this](const IndexRange range) { | IndexRange(node_states_.size()), 50, [&, this](const IndexRange range) { | ||||
| LinearAllocator<> &allocator = this->local_allocators_.local(); | LinearAllocator<> &allocator = this->local_allocators_.local(); | ||||
| for (const NodeWithState &item : node_states_.as_span().slice(range)) { | for (const NodeWithState &item : node_states_.as_span().slice(range)) { | ||||
| this->initialize_node_state(item.node, *item.state, allocator); | this->initialize_node_state(item.node, *item.state, allocator); | ||||
| } | } | ||||
| }); | }); | ||||
| /* Mark input sockets that have to be computed. */ | |||||
| for (const DSocket &socket : params_.force_compute_sockets) { | |||||
| NodeState &node_state = *node_states_.lookup_key_as(socket.node()).state; | |||||
| if (socket->is_input()) { | |||||
| node_state.inputs[socket->index()].force_compute = true; | |||||
| } | |||||
| } | |||||
| } | } | ||||
| void initialize_node_state(const DNode node, NodeState &node_state, LinearAllocator<> &allocator) | void initialize_node_state(const DNode node, NodeState &node_state, LinearAllocator<> &allocator) | ||||
| { | { | ||||
| /* Construct arrays of the correct size. */ | /* Construct arrays of the correct size. */ | ||||
| node_state.inputs = allocator.construct_array<InputState>(node->inputs().size()); | node_state.inputs = allocator.construct_array<InputState>(node->inputs().size()); | ||||
| node_state.outputs = allocator.construct_array<OutputState>(node->outputs().size()); | node_state.outputs = allocator.construct_array<OutputState>(node->outputs().size()); | ||||
| ▲ Show 20 Lines • Show All 230 Lines • ▼ Show 20 Lines | this->with_locked_node(node, node_state, [&](LockedNode &locked_node) { | ||||
| if (!this->prepare_node_inputs_for_execution(locked_node)) { | if (!this->prepare_node_inputs_for_execution(locked_node)) { | ||||
| return; | return; | ||||
| } | } | ||||
| do_execute_node = true; | do_execute_node = true; | ||||
| }); | }); | ||||
| return do_execute_node; | return do_execute_node; | ||||
| } | } | ||||
| /* A node is finished when it has computed all outputs that may be used. */ | /* A node is finished when it has computed all outputs that may be used have been computed and | ||||
| * when no input is still forced to be computed. */ | |||||
| bool finish_node_if_possible(LockedNode &locked_node) | bool finish_node_if_possible(LockedNode &locked_node) | ||||
| { | { | ||||
| if (locked_node.node_state.node_has_finished) { | if (locked_node.node_state.node_has_finished) { | ||||
| /* Early return in case this node is known to have finished already. */ | /* Early return in case this node is known to have finished already. */ | ||||
| return true; | return true; | ||||
| } | } | ||||
| /* Check if there is any output that might be used but has not been computed yet. */ | /* Check if there is any output that might be used but has not been computed yet. */ | ||||
| bool has_remaining_output = false; | |||||
| for (OutputState &output_state : locked_node.node_state.outputs) { | for (OutputState &output_state : locked_node.node_state.outputs) { | ||||
| if (output_state.has_been_computed) { | if (output_state.has_been_computed) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| if (output_state.output_usage != ValueUsage::Unused) { | if (output_state.output_usage != ValueUsage::Unused) { | ||||
| has_remaining_output = true; | return false; | ||||
| break; | |||||
| } | } | ||||
| } | } | ||||
| if (!has_remaining_output) { | |||||
| /* Check if there is an input that still has to be computed. */ | |||||
| for (InputState &input_state : locked_node.node_state.inputs) { | |||||
| if (input_state.force_compute) { | |||||
| if (!input_state.was_ready_for_execution) { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| } | |||||
| /* If there are no remaining outputs, all the inputs can be destructed and/or can become | /* If there are no remaining outputs, all the inputs can be destructed and/or can become | ||||
| * unused. This can also trigger a chain reaction where nodes to the left become finished | * unused. This can also trigger a chain reaction where nodes to the left become finished | ||||
| * too. */ | * too. */ | ||||
| for (const int i : locked_node.node->inputs().index_range()) { | for (const int i : locked_node.node->inputs().index_range()) { | ||||
| const DInputSocket socket = locked_node.node.input(i); | const DInputSocket socket = locked_node.node.input(i); | ||||
| InputState &input_state = locked_node.node_state.inputs[i]; | InputState &input_state = locked_node.node_state.inputs[i]; | ||||
| if (input_state.usage == ValueUsage::Maybe) { | if (input_state.usage == ValueUsage::Maybe) { | ||||
| this->set_input_unused(locked_node, socket); | this->set_input_unused(locked_node, socket); | ||||
| } | } | ||||
| else if (input_state.usage == ValueUsage::Required) { | else if (input_state.usage == ValueUsage::Required) { | ||||
| /* The value was required, so it cannot become unused. However, we can destruct the | /* The value was required, so it cannot become unused. However, we can destruct the | ||||
| * value. */ | * value. */ | ||||
| this->destruct_input_value_if_exists(locked_node, socket); | this->destruct_input_value_if_exists(locked_node, socket); | ||||
| } | } | ||||
| } | } | ||||
| locked_node.node_state.node_has_finished = true; | locked_node.node_state.node_has_finished = true; | ||||
| } | return true; | ||||
| return locked_node.node_state.node_has_finished; | |||||
| } | } | ||||
| bool prepare_node_outputs_for_execution(LockedNode &locked_node) | bool prepare_node_outputs_for_execution(LockedNode &locked_node) | ||||
| { | { | ||||
| bool execution_is_necessary = false; | bool execution_is_necessary = false; | ||||
| for (OutputState &output_state : locked_node.node_state.outputs) { | for (OutputState &output_state : locked_node.node_state.outputs) { | ||||
| /* Update the output usage for execution to the latest value. */ | /* Update the output usage for execution to the latest value. */ | ||||
| output_state.output_usage_for_execution = output_state.output_usage; | output_state.output_usage_for_execution = output_state.output_usage; | ||||
| ▲ Show 20 Lines • Show All 924 Lines • Show Last 20 Lines | |||||