diff options
Diffstat (limited to 'source/blender/modifiers/intern/MOD_nodes_evaluator.cc')
-rw-r--r-- | source/blender/modifiers/intern/MOD_nodes_evaluator.cc | 165 |
1 files changed, 96 insertions, 69 deletions
diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index 4e808120f4a..6b79c9e0f68 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -66,31 +66,55 @@ struct SingleInputValue { void *value = nullptr; }; -struct MultiInputValueItem { +struct MultiInputValue { /** - * The socket where this value is coming from. This is required to sort the inputs correctly - * based on the link order later on. + * Ordered sockets connected to this multi-input. */ - DSocket origin; + Vector<DSocket> origins; /** - * Should only be null directly after construction. After that it should always point to a value - * of the correct type. + * A value for every origin socket. The order is determined by #origins. + * Note, the same origin can occur multiple times. However, it is guaranteed that values coming + * from the same origin have the same value (the pointer is different, but they point to values + * that would compare equal). */ - void *value = nullptr; -}; - -struct MultiInputValue { + Vector<void *> values; /** - * Collection of all the inputs that have been provided already. Note, the same origin can occur - * multiple times. However, it is guaranteed that if two items have the same origin, they will - * also have the same value (the pointer is different, but they point to values that would - * compare equal). + * Number of non-null values. */ - Vector<MultiInputValueItem> items; - /** - * Number of items that need to be added until all inputs have been provided. - */ - int expected_size = 0; + int provided_value_count = 0; + + bool all_values_available() const + { + return this->missing_values() == 0; + } + + int missing_values() const + { + return this->values.size() - this->provided_value_count; + } + + void add_value(const DSocket origin, void *value) + { + const int index = this->find_available_index(origin); + this->values[index] = value; + this->provided_value_count++; + } + + private: + int find_available_index(DSocket origin) const + { + for (const int i : origins.index_range()) { + if (values[i] != nullptr) { + continue; + } + if (origins[i] != origin) { + continue; + } + return i; + } + BLI_assert_unreachable(); + return -1; + } }; struct InputState { @@ -211,9 +235,10 @@ struct NodeState { MutableSpan<OutputState> outputs; /** - * Nodes that don't support laziness have some special handling the first time they are executed. + * Most nodes have inputs that are always required. Those have special handling to avoid an extra + * call to the node execution function. */ - bool non_lazy_node_is_initialized = false; + bool non_lazy_inputs_handled = false; /** * Used to check that nodes that don't support laziness do not run more than once. @@ -556,13 +581,14 @@ class GeometryNodesEvaluator { /* Construct the correct struct that can hold the input(s). */ if (socket->is_multi_input_socket()) { input_state.value.multi = allocator.construct<MultiInputValue>().release(); + MultiInputValue &multi_value = *input_state.value.multi; /* Count how many values should be added until the socket is complete. */ - socket.foreach_origin_socket( - [&](DSocket UNUSED(origin)) { input_state.value.multi->expected_size++; }); + socket.foreach_origin_socket([&](DSocket origin) { multi_value.origins.append(origin); }); /* If no links are connected, we do read the value from socket itself. */ - if (input_state.value.multi->expected_size == 0) { - input_state.value.multi->expected_size = 1; + if (multi_value.origins.is_empty()) { + multi_value.origins.append(socket); } + multi_value.values.resize(multi_value.origins.size(), nullptr); } else { input_state.value.single = allocator.construct<SingleInputValue>().release(); @@ -623,8 +649,10 @@ class GeometryNodesEvaluator { const InputSocketRef &socket_ref = node->input(i); if (socket_ref.is_multi_input_socket()) { MultiInputValue &multi_value = *input_state.value.multi; - for (MultiInputValueItem &item : multi_value.items) { - input_state.type->destruct(item.value); + for (void *value : multi_value.values) { + if (value != nullptr) { + input_state.type->destruct(value); + } } multi_value.~MultiInputValue(); } @@ -774,12 +802,12 @@ class GeometryNodesEvaluator { if (!this->prepare_node_outputs_for_execution(locked_node)) { return; } - /* Initialize nodes that don't support laziness. This is done after at least one output is + /* Initialize inputs that don't support laziness. This is done after at least one output is * required and before we check that all required inputs are provided. This reduces the * number of "round-trips" through the task pool by one for most nodes. */ - if (!node_state.non_lazy_node_is_initialized && !node_supports_laziness(node)) { - this->initialize_non_lazy_node(locked_node); - node_state.non_lazy_node_is_initialized = true; + if (!node_state.non_lazy_inputs_handled) { + this->require_non_lazy_inputs(locked_node); + node_state.non_lazy_inputs_handled = true; } /* Prepare inputs and check if all required inputs are provided. */ if (!this->prepare_node_inputs_for_execution(locked_node)) { @@ -853,17 +881,27 @@ class GeometryNodesEvaluator { return execution_is_necessary; } - void initialize_non_lazy_node(LockedNode &locked_node) + void require_non_lazy_inputs(LockedNode &locked_node) { + this->foreach_non_lazy_input(locked_node, [&](const DInputSocket socket) { + this->set_input_required(locked_node, socket); + }); + } + + void foreach_non_lazy_input(LockedNode &locked_node, FunctionRef<void(DInputSocket socket)> fn) + { + if (node_supports_laziness(locked_node.node)) { + /* In the future only some of the inputs may support lazyness. */ + return; + } + /* Nodes that don't support laziness require all inputs. */ for (const int i : locked_node.node->inputs().index_range()) { InputState &input_state = locked_node.node_state.inputs[i]; if (input_state.type == nullptr) { /* Ignore unavailable/non-data sockets. */ continue; } - /* Nodes that don't support laziness require all inputs. */ - const DInputSocket input_socket = locked_node.node.input(i); - this->set_input_required(locked_node, input_socket); + fn(locked_node.node.input(i)); } } @@ -892,7 +930,7 @@ class GeometryNodesEvaluator { if (socket->is_multi_input_socket()) { MultiInputValue &multi_value = *input_state.value.multi; /* Checks if all the linked sockets have been provided already. */ - if (multi_value.items.size() == multi_value.expected_size) { + if (multi_value.all_values_available()) { input_state.was_ready_for_execution = true; } else if (is_required) { @@ -1243,7 +1281,7 @@ class GeometryNodesEvaluator { int missing_values = 0; if (input_socket->is_multi_input_socket()) { MultiInputValue &multi_value = *input_state.value.multi; - missing_values = multi_value.expected_size - multi_value.items.size(); + missing_values = multi_value.missing_values(); } else { SingleInputValue &single_value = *input_state.value.single; @@ -1501,10 +1539,10 @@ class GeometryNodesEvaluator { if (socket->is_multi_input_socket()) { /* Add a new value to the multi-input. */ MultiInputValue &multi_value = *input_state.value.multi; - multi_value.items.append({origin, value.get()}); + multi_value.add_value(origin, value.get()); - if (multi_value.expected_size == multi_value.items.size()) { - this->log_socket_value({socket}, input_state, multi_value.items); + if (multi_value.all_values_available()) { + this->log_socket_value({socket}, input_state, multi_value.values); } } else { @@ -1543,9 +1581,9 @@ class GeometryNodesEvaluator { GMutablePointer value = this->get_value_from_socket(origin_socket, *input_state.type); if (input_socket->is_multi_input_socket()) { MultiInputValue &multi_value = *input_state.value.multi; - multi_value.items.append({origin_socket, value.get()}); - if (multi_value.expected_size == multi_value.items.size()) { - this->log_socket_value({input_socket}, input_state, multi_value.items); + multi_value.add_value(origin_socket, value.get()); + if (multi_value.all_values_available()) { + this->log_socket_value({input_socket}, input_state, multi_value.values); } } else { @@ -1568,10 +1606,13 @@ class GeometryNodesEvaluator { InputState &input_state = locked_node.node_state.inputs[socket->index()]; if (socket->is_multi_input_socket()) { MultiInputValue &multi_value = *input_state.value.multi; - for (MultiInputValueItem &item : multi_value.items) { - input_state.type->destruct(item.value); + for (void *&value : multi_value.values) { + if (value != nullptr) { + input_state.type->destruct(value); + value = nullptr; + } } - multi_value.items.clear(); + multi_value.provided_value_count = 0; } else { SingleInputValue &single_value = *input_state.value.single; @@ -1653,7 +1694,7 @@ class GeometryNodesEvaluator { return *node_states_.lookup_key_as(node).state; } - void log_socket_value(DSocket socket, InputState &input_state, Span<MultiInputValueItem> values) + void log_socket_value(DSocket socket, InputState &input_state, Span<void *> values) { if (params_.geo_logger == nullptr) { return; @@ -1662,8 +1703,8 @@ class GeometryNodesEvaluator { Vector<GPointer, 16> value_pointers; value_pointers.reserve(values.size()); const CPPType &type = *input_state.type; - for (const MultiInputValueItem &item : values) { - value_pointers.append({type, item.value}); + for (const void *value : values) { + value_pointers.append({type, value}); } params_.geo_logger->local().log_multi_value_socket(socket, value_pointers); } @@ -1749,7 +1790,7 @@ bool NodeParamsProvider::can_get_input(StringRef identifier) const if (socket->is_multi_input_socket()) { MultiInputValue &multi_value = *input_state.value.multi; - return multi_value.items.size() == multi_value.expected_size; + return multi_value.all_values_available(); } SingleInputValue &single_value = *input_state.value.single; return single_value.value != nullptr; @@ -1789,25 +1830,11 @@ Vector<GMutablePointer> NodeParamsProvider::extract_multi_input(StringRef identi MultiInputValue &multi_value = *input_state.value.multi; Vector<GMutablePointer> ret_values; - socket.foreach_origin_socket([&](DSocket origin) { - for (MultiInputValueItem &item : multi_value.items) { - if (item.origin == origin && item.value != nullptr) { - ret_values.append({*input_state.type, item.value}); - /* Make sure we do not use the same value again if two values have the same origin. */ - item.value = nullptr; - return; - } - } - BLI_assert_unreachable(); - }); - if (ret_values.is_empty()) { - /* If the socket is not linked, we just use the value from the socket itself. */ - BLI_assert(multi_value.items.size() == 1); - MultiInputValueItem &item = multi_value.items[0]; - BLI_assert(item.origin == socket); - ret_values.append({*input_state.type, item.value}); - } - multi_value.items.clear(); + for (void *&value : multi_value.values) { + BLI_assert(value != nullptr); + ret_values.append({*input_state.type, value}); + value = nullptr; + } return ret_values; } |