From 98e6fe57f15a3d500dfd63be56187ef0f6eceb43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Mon, 17 Apr 2023 14:36:28 +0200 Subject: [PATCH 01/26] Support adding non-geometry sockets to simulation zone items. --- .../nodes/node_geo_simulation_output.cc | 99 ++++++++++++++----- 1 file changed, 75 insertions(+), 24 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc b/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc index 83bfa679dcc..81757825111 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc @@ -17,31 +17,63 @@ namespace blender::nodes { +static std::unique_ptr socket_declaration_for_simulation_item( + const NodeSimulationItem &item, eNodeSocketInOut in_out) +{ + std::unique_ptr decl; + switch (eNodeSocketDatatype(item.socket_type)) { + case SOCK_FLOAT: + decl = std::make_unique(); + break; + case SOCK_VECTOR: + decl = std::make_unique(); + break; + case SOCK_RGBA: + decl = std::make_unique(); + break; + case SOCK_BOOLEAN: + decl = std::make_unique(); + break; + case SOCK_INT: + decl = std::make_unique(); + break; + case SOCK_STRING: + decl = std::make_unique(); + break; + case SOCK_OBJECT: + decl = std::make_unique(); + break; + case SOCK_GEOMETRY: + decl = std::make_unique(); + break; + case SOCK_COLLECTION: + decl = std::make_unique(); + break; + case SOCK_TEXTURE: + decl = std::make_unique(); + break; + case SOCK_IMAGE: + decl = std::make_unique(); + break; + case SOCK_MATERIAL: + decl = std::make_unique(); + break; + default: + BLI_assert_unreachable(); + } + + decl->name = item.name; + decl->identifier = item.name; + decl->in_out = in_out; + return decl; +} + void socket_declarations_for_simulation_items(const Span items, NodeDeclaration &r_declaration) { for (const NodeSimulationItem &item : items) { - switch (eNodeSocketDatatype(item.socket_type)) { - case SOCK_GEOMETRY: { - { - std::unique_ptr decl = std::make_unique(); - decl->name = item.name; - decl->identifier = item.name; - decl->in_out = SOCK_IN; - r_declaration.inputs.append(std::move(decl)); - } - { - std::unique_ptr decl = std::make_unique(); - decl->name = item.name; - decl->identifier = item.name; - decl->in_out = SOCK_OUT; - r_declaration.outputs.append(std::move(decl)); - } - break; - } - default: - BLI_assert_unreachable(); - } + r_declaration.inputs.append(socket_declaration_for_simulation_item(item, SOCK_IN)); + r_declaration.outputs.append(socket_declaration_for_simulation_item(item, SOCK_OUT)); } r_declaration.inputs.append(decl::create_extend_declaration(SOCK_IN)); r_declaration.outputs.append(decl::create_extend_declaration(SOCK_OUT)); @@ -65,9 +97,6 @@ static bool simulation_items_unique_name_check(void *arg, const char *name) NodeSimulationItem *simulation_item_add_from_socket(NodeGeometrySimulationOutput &storage, const bNodeSocket &socket) { - if (socket.type != SOCK_GEOMETRY) { - return nullptr; - } char unique_name[MAX_NAME + 4] = ""; BLI_uniquename_cb(simulation_items_unique_name_check, &storage, @@ -96,8 +125,30 @@ NodeSimulationItem *simulation_item_add_from_socket(NodeGeometrySimulationOutput const CPPType &get_simulation_item_cpp_type(const NodeSimulationItem &item) { switch (item.socket_type) { + case SOCK_FLOAT: + return CPPType::get>(); + case SOCK_VECTOR: + return CPPType::get>(); + case SOCK_RGBA: + return CPPType::get>(); + case SOCK_BOOLEAN: + return CPPType::get>(); + case SOCK_INT: + return CPPType::get>(); + case SOCK_STRING: + return CPPType::get>(); + case SOCK_OBJECT: + return CPPType::get(); case SOCK_GEOMETRY: return CPPType::get(); + case SOCK_COLLECTION: + return CPPType::get(); + case SOCK_TEXTURE: + return CPPType::get(); + case SOCK_IMAGE: + return CPPType::get(); + case SOCK_MATERIAL: + return CPPType::get(); default: BLI_assert_unreachable(); return CPPType::get(); -- 2.30.2 From 9e58eba968307172d586cd36a0bc3a90fdb6a64b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Tue, 18 Apr 2023 18:48:58 +0200 Subject: [PATCH 02/26] Support state items with non-geometry types. --- .../blenkernel/BKE_simulation_state.hh | 78 +++++++++- .../nodes/node_geo_simulation_input.cc | 134 ++++++++++++++++-- .../nodes/node_geo_simulation_output.cc | 128 +++++++++++++++-- 3 files changed, 316 insertions(+), 24 deletions(-) diff --git a/source/blender/blenkernel/BKE_simulation_state.hh b/source/blender/blenkernel/BKE_simulation_state.hh index f7567c9baca..7082626ebac 100644 --- a/source/blender/blenkernel/BKE_simulation_state.hh +++ b/source/blender/blenkernel/BKE_simulation_state.hh @@ -13,21 +13,89 @@ class SimulationStateItem { virtual ~SimulationStateItem() = default; }; -class GeometrySimulationStateItem : public SimulationStateItem { +template +class TypedSimulationStateItem : public SimulationStateItem { + public: + using DataType = T; + private: - GeometrySet geometry_; + T data_; public: - GeometrySimulationStateItem(GeometrySet geometry) : geometry_(std::move(geometry)) + TypedSimulationStateItem() = default; + TypedSimulationStateItem(const T &data) { + data_ = std::move(data); + } + TypedSimulationStateItem(T &&data) + { + data_ = std::move(data); + } + template + TypedSimulationStateItem(const U &data) + { + data_ = data; } - const GeometrySet &geometry() const + TypedSimulationStateItem(const TypedSimulationStateItem &) = delete; + TypedSimulationStateItem(TypedSimulationStateItem &&) = delete; + + virtual ~TypedSimulationStateItem() { + } + + TypedSimulationStateItem &operator=(const TypedSimulationStateItem &) = delete; + TypedSimulationStateItem &operator=(TypedSimulationStateItem &&) = delete; + + const T &data() const { - return geometry_; + return data_; } }; +/** Specialization for GeometrySet which ensures the state owns the geometry data. */ +template <> +class TypedSimulationStateItem : public SimulationStateItem { + public: + using DataType = GeometrySet; + + private: + GeometrySet data_; + + public: + TypedSimulationStateItem() = default; + TypedSimulationStateItem(const GeometrySet &data) + { + data_ = std::move(data); + data_.ensure_owns_direct_data(); + } + TypedSimulationStateItem(GeometrySet &&data) + { + data_ = std::move(data); + data_.ensure_owns_direct_data(); + } + template + TypedSimulationStateItem(const U &data) + { + data_ = data; + data_.ensure_owns_direct_data(); + } + + TypedSimulationStateItem(const TypedSimulationStateItem &) = delete; + TypedSimulationStateItem(TypedSimulationStateItem &&) = delete; + + virtual ~TypedSimulationStateItem() { + } + + TypedSimulationStateItem &operator=(const TypedSimulationStateItem &) = delete; + TypedSimulationStateItem &operator=(TypedSimulationStateItem &&) = delete; + + const GeometrySet &data() const + { + return data_; + } +}; + + class SimulationZoneState { public: Vector> items; diff --git a/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc b/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc index bbcabd0d431..f78354af0c8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc @@ -12,6 +12,128 @@ #include "node_geometry_util.hh" +namespace blender::nodes { + +template +static void copy_typed_initial_simulation_state(lf::Params ¶ms, int index) +{ + T *data = params.try_get_input_data_ptr_or_request(index); + if (data != nullptr) { + params.set_output(index + 1, std::move(*data)); + } +} + +static void copy_initial_simulation_state( + lf::Params ¶ms, + int index, + short socket_type) +{ + switch (socket_type) { + case SOCK_FLOAT: + copy_typed_initial_simulation_state>(params, index); + break; + case SOCK_VECTOR: + copy_typed_initial_simulation_state>(params, index); + break; + case SOCK_RGBA: + copy_typed_initial_simulation_state>(params, index); + break; + case SOCK_BOOLEAN: + copy_typed_initial_simulation_state>(params, index); + break; + case SOCK_INT: + copy_typed_initial_simulation_state>(params, index); + break; + case SOCK_STRING: + copy_typed_initial_simulation_state>(params, index); + break; + case SOCK_OBJECT: + copy_typed_initial_simulation_state(params, index); + break; + case SOCK_GEOMETRY: + copy_typed_initial_simulation_state(params, index); + break; + case SOCK_COLLECTION: + copy_typed_initial_simulation_state(params, index); + break; + case SOCK_TEXTURE: + copy_typed_initial_simulation_state(params, index); + break; + case SOCK_IMAGE: + copy_typed_initial_simulation_state(params, index); + break; + case SOCK_MATERIAL: + copy_typed_initial_simulation_state(params, index); + break; + default: + BLI_assert_unreachable(); + copy_typed_initial_simulation_state(params, index); + break; + } +} + +template +static void copy_typed_next_simulation_state(lf::Params ¶ms, + int index, + const bke::sim::SimulationStateItem &state_item) +{ + if (auto *typed_state_item = dynamic_cast *>( + &state_item)) { + params.set_output(index + 1, typed_state_item->data()); + } +} + +static void copy_next_simulation_state(lf::Params ¶ms, + int index, + short socket_type, + const bke::sim::SimulationStateItem &state_item) +{ + switch (socket_type) { + case SOCK_FLOAT: + copy_typed_next_simulation_state>(params, index, state_item); + break; + case SOCK_VECTOR: + copy_typed_next_simulation_state>(params, index, state_item); + break; + case SOCK_RGBA: + copy_typed_next_simulation_state>(params, index, state_item); + break; + case SOCK_BOOLEAN: + copy_typed_next_simulation_state>(params, index, state_item); + break; + case SOCK_INT: + copy_typed_next_simulation_state>(params, index, state_item); + break; + case SOCK_STRING: + copy_typed_next_simulation_state>(params, index, state_item); + break; + case SOCK_OBJECT: + copy_typed_next_simulation_state(params, index, state_item); + break; + case SOCK_GEOMETRY: + copy_typed_next_simulation_state(params, index, state_item); + break; + case SOCK_COLLECTION: + copy_typed_next_simulation_state(params, index, state_item); + break; + case SOCK_TEXTURE: + copy_typed_next_simulation_state(params, index, state_item); + break; + case SOCK_IMAGE: + copy_typed_next_simulation_state(params, index, state_item); + break; + case SOCK_MATERIAL: + copy_typed_next_simulation_state(params, index, state_item); + break; + default: + BLI_assert_unreachable(); + copy_typed_next_simulation_state(params, index, state_item); + break; + } +} + +} + namespace blender::nodes::node_geo_simulation_input_cc { NODE_STORAGE_FUNCS(NodeGeometrySimulationInput); @@ -63,10 +185,7 @@ class LazyFunctionForSimulationInputNode final : public LazyFunction { if (params.output_was_set(i + 1)) { continue; } - GeometrySet *geometry = params.try_get_input_data_ptr_or_request(i); - if (geometry != nullptr) { - params.set_output(i + 1, std::move(*geometry)); - } + copy_initial_simulation_state(params, i, simulation_items_[i].socket_type); } } else { @@ -75,10 +194,9 @@ class LazyFunctionForSimulationInputNode final : public LazyFunction { params.set_output(i + 1, GeometrySet()); continue; } - const bke::sim::SimulationStateItem *item = prev_zone_state->items[i].get(); - if (auto *geometry_item = dynamic_cast( - item)) { - params.set_output(i + 1, geometry_item->geometry()); + const bke::sim::SimulationStateItem *state_item = prev_zone_state->items[i].get(); + if (state_item != nullptr) { + copy_next_simulation_state(params, i, simulation_items_[i].socket_type, *state_item); } } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc b/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc index 81757825111..f8fbd509ca3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc @@ -155,6 +155,112 @@ const CPPType &get_simulation_item_cpp_type(const NodeSimulationItem &item) } } +template +static std::unique_ptr> make_typed_simulation_state_item(lf::Params ¶ms, int index) +{ + using bke::sim::TypedSimulationStateItem; + + if (const T *data = params.try_get_input_data_ptr_or_request(index)) { + return std::make_unique>(*data); + } + + return std::make_unique>(); +} + +static std::unique_ptr make_simulation_state_item( + lf::Params ¶ms, int index, short socket_type) +{ + switch (socket_type) { + case SOCK_FLOAT: + return make_typed_simulation_state_item>(params, index); + case SOCK_VECTOR: + return make_typed_simulation_state_item>(params, index); + case SOCK_RGBA: + return make_typed_simulation_state_item>(params, index); + case SOCK_BOOLEAN: + return make_typed_simulation_state_item>(params, index); + case SOCK_INT: + return make_typed_simulation_state_item>(params, index); + case SOCK_STRING: + return make_typed_simulation_state_item>(params, index); + case SOCK_OBJECT: + return make_typed_simulation_state_item(params, index); + case SOCK_GEOMETRY: + return make_typed_simulation_state_item(params, index); + case SOCK_COLLECTION: + return make_typed_simulation_state_item(params, index); + case SOCK_TEXTURE: + return make_typed_simulation_state_item(params, index); + case SOCK_IMAGE: + return make_typed_simulation_state_item(params, index); + case SOCK_MATERIAL: + return make_typed_simulation_state_item(params, index); + default: + BLI_assert_unreachable(); + return make_typed_simulation_state_item(params, index); + } +} + +template +static void copy_typed_simulation_state_output(lf::Params ¶ms, int index, const bke::sim::SimulationStateItem &state_item) +{ + using bke::sim::TypedSimulationStateItem; + + if (auto *typed_state_item = dynamic_cast *>(&state_item)) { + params.set_output(index, typed_state_item->data()); + } +} + +static void copy_simulation_state_output( + lf::Params ¶ms, + int index, + short socket_type, + const bke::sim::SimulationStateItem &state_item) +{ + switch (socket_type) { + case SOCK_FLOAT: + copy_typed_simulation_state_output>(params, index, state_item); + break; + case SOCK_VECTOR: + copy_typed_simulation_state_output>(params, index, state_item); + break; + case SOCK_RGBA: + copy_typed_simulation_state_output>(params, index, state_item); + break; + case SOCK_BOOLEAN: + copy_typed_simulation_state_output>(params, index, state_item); + break; + case SOCK_INT: + copy_typed_simulation_state_output>(params, index, state_item); + break; + case SOCK_STRING: + copy_typed_simulation_state_output>(params, index, state_item); + break; + case SOCK_OBJECT: + copy_typed_simulation_state_output(params, index, state_item); + break; + case SOCK_GEOMETRY: + copy_typed_simulation_state_output(params, index, state_item); + break; + case SOCK_COLLECTION: + copy_typed_simulation_state_output(params, index, state_item); + break; + case SOCK_TEXTURE: + copy_typed_simulation_state_output(params, index, state_item); + break; + case SOCK_IMAGE: + copy_typed_simulation_state_output(params, index, state_item); + break; + case SOCK_MATERIAL: + copy_typed_simulation_state_output(params, index, state_item); + break; + default: + BLI_assert_unreachable(); + copy_typed_simulation_state_output(params, index, state_item); + break; + } +} + } // namespace blender::nodes namespace blender::nodes::node_geo_simulation_output_cc { @@ -224,14 +330,15 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction { bool all_available = true; for (const int i : simulation_items_.index_range()) { - GeometrySet *input_geometry = params.try_get_input_data_ptr_or_request(i); - if (input_geometry == nullptr) { + const NodeSimulationItem &item = simulation_items_[i]; + + void *input_data = params.try_get_input_data_ptr_or_request(i); + if (input_data == nullptr) { all_available = false; continue; } - input_geometry->ensure_owns_direct_data(); - new_zone_state.items[i] = std::make_unique( - std::move(*input_geometry)); + + new_zone_state.items[i] = make_simulation_state_item(params, i, item.socket_type); } if (all_available) { @@ -242,17 +349,16 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction { void output_cached_state(lf::Params ¶ms, const bke::sim::SimulationZoneState &state) const { for (const int i : simulation_items_.index_range()) { + const NodeSimulationItem &item = simulation_items_[i]; + if (i >= state.items.size()) { continue; } - const bke::sim::SimulationStateItem *item = state.items[i].get(); - if (item == nullptr) { + const bke::sim::SimulationStateItem *state_item = state.items[i].get(); + if (state_item == nullptr) { continue; } - if (auto *geometry_item = dynamic_cast( - item)) { - params.set_output(i, geometry_item->geometry()); - } + copy_simulation_state_output(params, i, item.socket_type, *state_item); } params.set_default_remaining_outputs(); } -- 2.30.2 From 057ccfa22a42a37ccdb38bc5736e119d50a54c66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Wed, 19 Apr 2023 11:42:19 +0200 Subject: [PATCH 03/26] Add nominal field support to the sockets generated by simulation nodes. --- .../nodes/node_geo_simulation_output.cc | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc b/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc index f8fbd509ca3..0c2ca55dd96 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc @@ -18,24 +18,34 @@ namespace blender::nodes { static std::unique_ptr socket_declaration_for_simulation_item( - const NodeSimulationItem &item, eNodeSocketInOut in_out) + const NodeSimulationItem &item, eNodeSocketInOut in_out, int index) { std::unique_ptr decl; switch (eNodeSocketDatatype(item.socket_type)) { case SOCK_FLOAT: decl = std::make_unique(); + decl->input_field_type = InputSocketFieldType::IsSupported; + decl->output_field_dependency = OutputFieldDependency::ForPartiallyDependentField({index}); break; case SOCK_VECTOR: decl = std::make_unique(); + decl->input_field_type = InputSocketFieldType::IsSupported; + decl->output_field_dependency = OutputFieldDependency::ForPartiallyDependentField({index}); break; case SOCK_RGBA: decl = std::make_unique(); + decl->input_field_type = InputSocketFieldType::IsSupported; + decl->output_field_dependency = OutputFieldDependency::ForPartiallyDependentField({index}); break; case SOCK_BOOLEAN: decl = std::make_unique(); + decl->input_field_type = InputSocketFieldType::IsSupported; + decl->output_field_dependency = OutputFieldDependency::ForPartiallyDependentField({index}); break; case SOCK_INT: decl = std::make_unique(); + decl->input_field_type = InputSocketFieldType::IsSupported; + decl->output_field_dependency = OutputFieldDependency::ForPartiallyDependentField({index}); break; case SOCK_STRING: decl = std::make_unique(); @@ -71,9 +81,10 @@ static std::unique_ptr socket_declaration_for_simulation_item void socket_declarations_for_simulation_items(const Span items, NodeDeclaration &r_declaration) { - for (const NodeSimulationItem &item : items) { - r_declaration.inputs.append(socket_declaration_for_simulation_item(item, SOCK_IN)); - r_declaration.outputs.append(socket_declaration_for_simulation_item(item, SOCK_OUT)); + for (const int i : items.index_range()) { + const NodeSimulationItem &item = items[i]; + r_declaration.inputs.append(socket_declaration_for_simulation_item(item, SOCK_IN, i)); + r_declaration.outputs.append(socket_declaration_for_simulation_item(item, SOCK_OUT, i)); } r_declaration.inputs.append(decl::create_extend_declaration(SOCK_IN)); r_declaration.outputs.append(decl::create_extend_declaration(SOCK_OUT)); -- 2.30.2 From 15b9a3d099f70ef2b92d43bae6d5aebd7beb6235 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Wed, 19 Apr 2023 15:08:12 +0200 Subject: [PATCH 04/26] Modified field state propagation function to include simulation zones. Simulation input/output dependencies can create dependency cycles that take several passes to resolve. New passes are only scheduled when the field state actually changes, so once input and output agree on a minimum viable field state the updates are complete. --- .../intern/node_tree_field_inferencing.cc | 253 ++++++++++++++---- 1 file changed, 203 insertions(+), 50 deletions(-) diff --git a/source/blender/blenkernel/intern/node_tree_field_inferencing.cc b/source/blender/blenkernel/intern/node_tree_field_inferencing.cc index 067745e0ad6..db30184e9b0 100644 --- a/source/blender/blenkernel/intern/node_tree_field_inferencing.cc +++ b/source/blender/blenkernel/intern/node_tree_field_inferencing.cc @@ -256,6 +256,141 @@ static OutputFieldDependency find_group_output_dependencies( return OutputFieldDependency::ForPartiallyDependentField(std::move(linked_input_indices)); } +/** Result of syncing two field states. */ +enum eFieldStateSyncResult { + /* State A has been modified. */ + CHANGED_A = (1 << 0), + /* State B has been modified. */ + CHANGED_B = (1 << 1), +}; + +/** + * Compare both field states and select the most compatible. + * Afterwards both field states will be the same. + * @return eFieldStateSyncResult flags indicating which field states have changed. + */ +static int sync_field_states(SocketFieldState &a, SocketFieldState &b) +{ + int res = 0; + if (a.is_field_source != b.is_field_source) { + if (a.is_field_source) { + b.is_field_source = true; + res |= eFieldStateSyncResult::CHANGED_B; + } + else { + a.is_field_source = true; + res |= eFieldStateSyncResult::CHANGED_A; + } + } + if (a.requires_single != b.requires_single) { + if (a.requires_single) { + b.requires_single = true; + res |= eFieldStateSyncResult::CHANGED_B; + } + else { + a.requires_single = true; + res |= eFieldStateSyncResult::CHANGED_A; + } + } + if (a.is_always_single != b.is_always_single) { + if (a.is_always_single) { + b.is_always_single = true; + b.is_single = true; + res |= eFieldStateSyncResult::CHANGED_B; + } + else { + a.is_always_single = true; + a.is_single = true; + res |= eFieldStateSyncResult::CHANGED_A; + } + } + else if (!a.is_always_single) { + /* Can be single value without always being one. */ + if (a.is_single != b.is_single) { + if (a.is_single) { + b.is_single = true; + res |= eFieldStateSyncResult::CHANGED_B; + } + else { + a.is_single = true; + res |= eFieldStateSyncResult::CHANGED_A; + } + } + } + else { + BLI_assert(a.is_single); + BLI_assert(b.is_single); + } + return res; +} + +/** + * Compare field states of simulation nodes sockets and select the most compatible. + * Afterwards all field states will be the same. + * @return eFieldStateSyncResult flags indicating which field states have changed. + */ +static int simulation_nodes_field_state_sync( + const bNode &input_node, + const bNode &output_node, + const MutableSpan field_state_by_socket_id) +{ + int res = 0; + for (const int i : output_node.input_sockets().index_range()) { + const bNodeSocket *input_socket = input_node.input_sockets()[i]; + const bNodeSocket *output_socket = output_node.input_sockets()[i]; + SocketFieldState &input_state = field_state_by_socket_id[input_socket->index_in_tree()]; + SocketFieldState &output_state = field_state_by_socket_id[output_socket->index_in_tree()]; + res |= sync_field_states(input_state, output_state); + } + for (const int i : output_node.output_sockets().index_range()) { + /* First input node output is Delta Time which does not appear in the output node outputs. */ + const bNodeSocket *input_socket = input_node.output_sockets()[i + 1]; + const bNodeSocket *output_socket = output_node.output_sockets()[i]; + SocketFieldState &input_state = field_state_by_socket_id[input_socket->index_in_tree()]; + SocketFieldState &output_state = field_state_by_socket_id[output_socket->index_in_tree()]; + res |= sync_field_states(input_state, output_state); + } + return res; +} + +static bool propagate_special_data_requirements( + const bNodeTree &tree, + const bNode &node, + const MutableSpan field_state_by_socket_id) +{ + tree.ensure_topology_cache(); + + bool need_update = false; + + /* Sync field state between simulation nodes and schedule another pass if necessary. */ + if (node.type == GEO_NODE_SIMULATION_INPUT) { + const NodeGeometrySimulationInput &data = *static_cast( + node.storage); + if (const bNode *output_node = tree.node_by_id(data.output_node_id)) { + int sync_result = simulation_nodes_field_state_sync( + node, *output_node, field_state_by_socket_id); + if (sync_result & eFieldStateSyncResult::CHANGED_B) { + need_update = true; + } + } + } + else if (node.type == GEO_NODE_SIMULATION_OUTPUT) { + for (const bNode *input_node : tree.nodes_by_type("GeometryNodeSimulationInput")) { + const NodeGeometrySimulationInput &data = *static_cast( + input_node->storage); + if (node.identifier == data.output_node_id) { + int sync_result = simulation_nodes_field_state_sync( + *input_node, node, field_state_by_socket_id); + if (sync_result & eFieldStateSyncResult::CHANGED_A) { + need_update = true; + } + } + } + } + + return need_update; +} + static void propagate_data_requirements_from_right_to_left( const bNodeTree &tree, const Span interface_by_node, @@ -263,70 +398,88 @@ static void propagate_data_requirements_from_right_to_left( { const Span toposort_result = tree.toposort_right_to_left(); - for (const bNode *node : toposort_result) { - const FieldInferencingInterface &inferencing_interface = *interface_by_node[node->index()]; + while (true) { + /* Node updates may require sevaral passes due to cyclic dependencies. + * Specifically, a simulation input node may change simulation output node field state, + * which requires re-running the update starting at the output node (doing full passes for simplicity). + * This can happen at most once per simulation zone. */ + bool need_update = false; - for (const bNodeSocket *output_socket : node->output_sockets()) { - SocketFieldState &state = field_state_by_socket_id[output_socket->index_in_tree()]; + for (const bNode *node : toposort_result) { + const FieldInferencingInterface &inferencing_interface = *interface_by_node[node->index()]; - const OutputFieldDependency &field_dependency = - inferencing_interface.outputs[output_socket->index()]; + for (const bNodeSocket *output_socket : node->output_sockets()) { + SocketFieldState &state = field_state_by_socket_id[output_socket->index_in_tree()]; - if (field_dependency.field_type() == OutputSocketFieldType::FieldSource) { - continue; - } - if (field_dependency.field_type() == OutputSocketFieldType::None) { - state.requires_single = true; - state.is_always_single = true; - continue; - } + const OutputFieldDependency &field_dependency = + inferencing_interface.outputs[output_socket->index()]; - /* The output is required to be a single value when it is connected to any input that does - * not support fields. */ - for (const bNodeSocket *target_socket : output_socket->directly_linked_sockets()) { - if (target_socket->is_available()) { - state.requires_single |= - field_state_by_socket_id[target_socket->index_in_tree()].requires_single; + if (field_dependency.field_type() == OutputSocketFieldType::FieldSource) { + continue; + } + if (field_dependency.field_type() == OutputSocketFieldType::None) { + state.requires_single = true; + state.is_always_single = true; + continue; } - } - if (state.requires_single) { - bool any_input_is_field_implicitly = false; - const Vector connected_inputs = gather_input_socket_dependencies( - field_dependency, *node); - for (const bNodeSocket *input_socket : connected_inputs) { - if (!input_socket->is_available()) { - continue; + /* The output is required to be a single value when it is connected to any input that does + * not support fields. */ + for (const bNodeSocket *target_socket : output_socket->directly_linked_sockets()) { + if (target_socket->is_available()) { + state.requires_single |= + field_state_by_socket_id[target_socket->index_in_tree()].requires_single; } - if (inferencing_interface.inputs[input_socket->index()] == - InputSocketFieldType::Implicit) { - if (!input_socket->is_logically_linked()) { - any_input_is_field_implicitly = true; - break; + } + + if (state.requires_single) { + bool any_input_is_field_implicitly = false; + const Vector connected_inputs = gather_input_socket_dependencies( + field_dependency, *node); + for (const bNodeSocket *input_socket : connected_inputs) { + if (!input_socket->is_available()) { + continue; + } + if (inferencing_interface.inputs[input_socket->index()] == + InputSocketFieldType::Implicit) { + if (!input_socket->is_logically_linked()) { + any_input_is_field_implicitly = true; + break; + } + } + } + if (any_input_is_field_implicitly) { + /* This output isn't a single value actually. */ + state.requires_single = false; + } + else { + /* If the output is required to be a single value, the connected inputs in the same + * node must not be fields as well. */ + for (const bNodeSocket *input_socket : connected_inputs) { + field_state_by_socket_id[input_socket->index_in_tree()].requires_single = true; } } } - if (any_input_is_field_implicitly) { - /* This output isn't a single value actually. */ - state.requires_single = false; - } - else { - /* If the output is required to be a single value, the connected inputs in the same node - * must not be fields as well. */ - for (const bNodeSocket *input_socket : connected_inputs) { - field_state_by_socket_id[input_socket->index_in_tree()].requires_single = true; - } + } + + /* Some inputs do not require fields independent of what the outputs are connected to. */ + for (const bNodeSocket *input_socket : node->input_sockets()) { + SocketFieldState &state = field_state_by_socket_id[input_socket->index_in_tree()]; + if (inferencing_interface.inputs[input_socket->index()] == InputSocketFieldType::None) { + state.requires_single = true; + state.is_always_single = true; } } + + /* Find reverse dependencies and resolve conflicts, which may require another pass. */ + if (propagate_special_data_requirements(tree, *node, field_state_by_socket_id)) + { + need_update = true; + } } - /* Some inputs do not require fields independent of what the outputs are connected to. */ - for (const bNodeSocket *input_socket : node->input_sockets()) { - SocketFieldState &state = field_state_by_socket_id[input_socket->index_in_tree()]; - if (inferencing_interface.inputs[input_socket->index()] == InputSocketFieldType::None) { - state.requires_single = true; - state.is_always_single = true; - } + if (!need_update) { + break; } } } -- 2.30.2 From b5c9acf8c77301320a3a750356c93c4bc0971d78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Wed, 19 Apr 2023 15:38:25 +0200 Subject: [PATCH 05/26] Forward propagation of field state including special sim zone dependencies. --- .../intern/node_tree_field_inferencing.cc | 127 ++++++++++-------- 1 file changed, 70 insertions(+), 57 deletions(-) diff --git a/source/blender/blenkernel/intern/node_tree_field_inferencing.cc b/source/blender/blenkernel/intern/node_tree_field_inferencing.cc index db30184e9b0..dfc944b48f0 100644 --- a/source/blender/blenkernel/intern/node_tree_field_inferencing.cc +++ b/source/blender/blenkernel/intern/node_tree_field_inferencing.cc @@ -305,14 +305,15 @@ static int sync_field_states(SocketFieldState &a, SocketFieldState &b) } } else if (!a.is_always_single) { - /* Can be single value without always being one. */ + /* Can be single value without always being one. + * Field (non-single) is preferred in this case. */ if (a.is_single != b.is_single) { - if (a.is_single) { - b.is_single = true; + if (!a.is_single) { + b.is_single = false; res |= eFieldStateSyncResult::CHANGED_B; } else { - a.is_single = true; + a.is_single = false; res |= eFieldStateSyncResult::CHANGED_A; } } @@ -399,10 +400,7 @@ static void propagate_data_requirements_from_right_to_left( const Span toposort_result = tree.toposort_right_to_left(); while (true) { - /* Node updates may require sevaral passes due to cyclic dependencies. - * Specifically, a simulation input node may change simulation output node field state, - * which requires re-running the update starting at the output node (doing full passes for simplicity). - * This can happen at most once per simulation zone. */ + /* Node updates may require sevaral passes due to cyclic dependencies. */ bool need_update = false; for (const bNode *node : toposort_result) { @@ -535,68 +533,83 @@ static void propagate_field_status_from_left_to_right( { const Span toposort_result = tree.toposort_left_to_right(); - for (const bNode *node : toposort_result) { - if (node->type == NODE_GROUP_INPUT) { - continue; - } + while (true) { + /* Node updates may require sevaral passes due to cyclic dependencies. */ + bool need_update = false; - const FieldInferencingInterface &inferencing_interface = *interface_by_node[node->index()]; - - /* Update field state of input sockets, also taking into account linked origin sockets. */ - for (const bNodeSocket *input_socket : node->input_sockets()) { - SocketFieldState &state = field_state_by_socket_id[input_socket->index_in_tree()]; - if (state.is_always_single) { - state.is_single = true; + for (const bNode *node : toposort_result) { + if (node->type == NODE_GROUP_INPUT) { continue; } - state.is_single = true; - if (!input_socket->is_directly_linked()) { - if (inferencing_interface.inputs[input_socket->index()] == - InputSocketFieldType::Implicit) { - state.is_single = false; + + const FieldInferencingInterface &inferencing_interface = *interface_by_node[node->index()]; + + /* Update field state of input sockets, also taking into account linked origin sockets. */ + for (const bNodeSocket *input_socket : node->input_sockets()) { + SocketFieldState &state = field_state_by_socket_id[input_socket->index_in_tree()]; + if (state.is_always_single) { + state.is_single = true; + continue; } - } - else { - for (const bNodeSocket *origin_socket : input_socket->directly_linked_sockets()) { - if (!field_state_by_socket_id[origin_socket->index_in_tree()].is_single) { + state.is_single = true; + if (!input_socket->is_directly_linked()) { + if (inferencing_interface.inputs[input_socket->index()] == + InputSocketFieldType::Implicit) { state.is_single = false; - break; } } - } - } - - /* Update field state of output sockets, also taking into account input sockets. */ - for (const bNodeSocket *output_socket : node->output_sockets()) { - SocketFieldState &state = field_state_by_socket_id[output_socket->index_in_tree()]; - const OutputFieldDependency &field_dependency = - inferencing_interface.outputs[output_socket->index()]; - - switch (field_dependency.field_type()) { - case OutputSocketFieldType::None: { - state.is_single = true; - break; - } - case OutputSocketFieldType::FieldSource: { - state.is_single = false; - state.is_field_source = true; - break; - } - case OutputSocketFieldType::PartiallyDependent: - case OutputSocketFieldType::DependentField: { - for (const bNodeSocket *input_socket : - gather_input_socket_dependencies(field_dependency, *node)) { - if (!input_socket->is_available()) { - continue; - } - if (!field_state_by_socket_id[input_socket->index_in_tree()].is_single) { + else { + for (const bNodeSocket *origin_socket : input_socket->directly_linked_sockets()) { + if (!field_state_by_socket_id[origin_socket->index_in_tree()].is_single) { state.is_single = false; break; } } - break; } } + + /* Update field state of output sockets, also taking into account input sockets. */ + for (const bNodeSocket *output_socket : node->output_sockets()) { + SocketFieldState &state = field_state_by_socket_id[output_socket->index_in_tree()]; + const OutputFieldDependency &field_dependency = + inferencing_interface.outputs[output_socket->index()]; + + switch (field_dependency.field_type()) { + case OutputSocketFieldType::None: { + state.is_single = true; + break; + } + case OutputSocketFieldType::FieldSource: { + state.is_single = false; + state.is_field_source = true; + break; + } + case OutputSocketFieldType::PartiallyDependent: + case OutputSocketFieldType::DependentField: { + for (const bNodeSocket *input_socket : + gather_input_socket_dependencies(field_dependency, *node)) { + if (!input_socket->is_available()) { + continue; + } + if (!field_state_by_socket_id[input_socket->index_in_tree()].is_single) { + state.is_single = false; + break; + } + } + break; + } + } + } + + /* Find reverse dependencies and resolve conflicts, which may require another pass. */ + if (propagate_special_data_requirements(tree, *node, field_state_by_socket_id)) + { + need_update = true; + } + } + + if (!need_update) { + break; } } } -- 2.30.2 From 5681327da9ea4b2e63127f0cb7be1ae921fcac52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Wed, 19 Apr 2023 16:27:09 +0200 Subject: [PATCH 06/26] Fix merge error. --- source/blender/blenkernel/BKE_simulation_state.hh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/blenkernel/BKE_simulation_state.hh b/source/blender/blenkernel/BKE_simulation_state.hh index deb9ce68fff..7082626ebac 100644 --- a/source/blender/blenkernel/BKE_simulation_state.hh +++ b/source/blender/blenkernel/BKE_simulation_state.hh @@ -28,12 +28,14 @@ class TypedSimulationStateItem : public SimulationStateItem { data_ = std::move(data); } TypedSimulationStateItem(T &&data) + { data_ = std::move(data); } template TypedSimulationStateItem(const U &data) { data_ = data; + } TypedSimulationStateItem(const TypedSimulationStateItem &) = delete; TypedSimulationStateItem(TypedSimulationStateItem &&) = delete; -- 2.30.2 From 60f1e7fe765a1714cf0606cf54498d4c2ce38863 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Thu, 20 Apr 2023 09:39:43 +0200 Subject: [PATCH 07/26] Simplified field state syncing. Only the `requires_single` flag actually needs propagation. `is_single` flag remains local to either node, depending on whether a single-value input is connected or not. `is_always_single` flag is dependent only on the socket type. `is_field_source` is always false for the simulation zone sockets. --- .../intern/node_tree_field_inferencing.cc | 70 ++++++------------- 1 file changed, 22 insertions(+), 48 deletions(-) diff --git a/source/blender/blenkernel/intern/node_tree_field_inferencing.cc b/source/blender/blenkernel/intern/node_tree_field_inferencing.cc index dfc944b48f0..af44b983e3d 100644 --- a/source/blender/blenkernel/intern/node_tree_field_inferencing.cc +++ b/source/blender/blenkernel/intern/node_tree_field_inferencing.cc @@ -156,6 +156,18 @@ struct SocketFieldState { * supports this socket to be a single value, or because a node afterwards requires this to be a * single value. */ bool requires_single = false; + + bool operator==(const SocketFieldState &other) + { + return this->is_field_source == other.is_field_source && + this->is_always_single == other.is_always_single && + this->is_single == other.is_single && this->requires_single == other.requires_single; + } + + bool operator!=(const SocketFieldState &other) + { + return !(*this == other); + } }; static Vector gather_input_socket_dependencies( @@ -271,57 +283,19 @@ enum eFieldStateSyncResult { */ static int sync_field_states(SocketFieldState &a, SocketFieldState &b) { + const bool requires_single = a.requires_single || b.requires_single; + int res = 0; - if (a.is_field_source != b.is_field_source) { - if (a.is_field_source) { - b.is_field_source = true; - res |= eFieldStateSyncResult::CHANGED_B; - } - else { - a.is_field_source = true; - res |= eFieldStateSyncResult::CHANGED_A; - } + if (a.requires_single != requires_single) { + res |= eFieldStateSyncResult::CHANGED_A; } - if (a.requires_single != b.requires_single) { - if (a.requires_single) { - b.requires_single = true; - res |= eFieldStateSyncResult::CHANGED_B; - } - else { - a.requires_single = true; - res |= eFieldStateSyncResult::CHANGED_A; - } - } - if (a.is_always_single != b.is_always_single) { - if (a.is_always_single) { - b.is_always_single = true; - b.is_single = true; - res |= eFieldStateSyncResult::CHANGED_B; - } - else { - a.is_always_single = true; - a.is_single = true; - res |= eFieldStateSyncResult::CHANGED_A; - } - } - else if (!a.is_always_single) { - /* Can be single value without always being one. - * Field (non-single) is preferred in this case. */ - if (a.is_single != b.is_single) { - if (!a.is_single) { - b.is_single = false; - res |= eFieldStateSyncResult::CHANGED_B; - } - else { - a.is_single = false; - res |= eFieldStateSyncResult::CHANGED_A; - } - } - } - else { - BLI_assert(a.is_single); - BLI_assert(b.is_single); + if (b.requires_single != requires_single) { + res |= eFieldStateSyncResult::CHANGED_B; } + + a.requires_single = requires_single; + b.requires_single = requires_single; + return res; } -- 2.30.2 From aa9c2b11f6b29a0de33e768a03dda595c663c021 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Thu, 20 Apr 2023 11:19:39 +0200 Subject: [PATCH 08/26] Use backslash style of doxygen syntax. --- .../blender/blenkernel/intern/node_tree_field_inferencing.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/node_tree_field_inferencing.cc b/source/blender/blenkernel/intern/node_tree_field_inferencing.cc index af44b983e3d..a1f4a531627 100644 --- a/source/blender/blenkernel/intern/node_tree_field_inferencing.cc +++ b/source/blender/blenkernel/intern/node_tree_field_inferencing.cc @@ -279,7 +279,7 @@ enum eFieldStateSyncResult { /** * Compare both field states and select the most compatible. * Afterwards both field states will be the same. - * @return eFieldStateSyncResult flags indicating which field states have changed. + * \return eFieldStateSyncResult flags indicating which field states have changed. */ static int sync_field_states(SocketFieldState &a, SocketFieldState &b) { @@ -302,7 +302,7 @@ static int sync_field_states(SocketFieldState &a, SocketFieldState &b) /** * Compare field states of simulation nodes sockets and select the most compatible. * Afterwards all field states will be the same. - * @return eFieldStateSyncResult flags indicating which field states have changed. + * \return eFieldStateSyncResult flags indicating which field states have changed. */ static int simulation_nodes_field_state_sync( const bNode &input_node, -- 2.30.2 From 695258c4d44c0468f190c0b5ee44951d899e8598 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Thu, 20 Apr 2023 11:27:46 +0200 Subject: [PATCH 09/26] ENUM_OPERATORS for eFieldStateSyncResult to use it as a flag instead of int. --- .../intern/node_tree_field_inferencing.cc | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/source/blender/blenkernel/intern/node_tree_field_inferencing.cc b/source/blender/blenkernel/intern/node_tree_field_inferencing.cc index a1f4a531627..911ce15a16a 100644 --- a/source/blender/blenkernel/intern/node_tree_field_inferencing.cc +++ b/source/blender/blenkernel/intern/node_tree_field_inferencing.cc @@ -275,17 +275,18 @@ enum eFieldStateSyncResult { /* State B has been modified. */ CHANGED_B = (1 << 1), }; +ENUM_OPERATORS(eFieldStateSyncResult, CHANGED_B) /** * Compare both field states and select the most compatible. * Afterwards both field states will be the same. * \return eFieldStateSyncResult flags indicating which field states have changed. */ -static int sync_field_states(SocketFieldState &a, SocketFieldState &b) +static eFieldStateSyncResult sync_field_states(SocketFieldState &a, SocketFieldState &b) { const bool requires_single = a.requires_single || b.requires_single; - int res = 0; + eFieldStateSyncResult res = static_cast(0); if (a.requires_single != requires_single) { res |= eFieldStateSyncResult::CHANGED_A; } @@ -304,12 +305,12 @@ static int sync_field_states(SocketFieldState &a, SocketFieldState &b) * Afterwards all field states will be the same. * \return eFieldStateSyncResult flags indicating which field states have changed. */ -static int simulation_nodes_field_state_sync( +static eFieldStateSyncResult simulation_nodes_field_state_sync( const bNode &input_node, const bNode &output_node, const MutableSpan field_state_by_socket_id) { - int res = 0; + eFieldStateSyncResult res = static_cast(0); for (const int i : output_node.input_sockets().index_range()) { const bNodeSocket *input_socket = input_node.input_sockets()[i]; const bNodeSocket *output_socket = output_node.input_sockets()[i]; @@ -342,7 +343,7 @@ static bool propagate_special_data_requirements( const NodeGeometrySimulationInput &data = *static_cast( node.storage); if (const bNode *output_node = tree.node_by_id(data.output_node_id)) { - int sync_result = simulation_nodes_field_state_sync( + eFieldStateSyncResult sync_result = simulation_nodes_field_state_sync( node, *output_node, field_state_by_socket_id); if (sync_result & eFieldStateSyncResult::CHANGED_B) { need_update = true; @@ -354,7 +355,7 @@ static bool propagate_special_data_requirements( const NodeGeometrySimulationInput &data = *static_cast( input_node->storage); if (node.identifier == data.output_node_id) { - int sync_result = simulation_nodes_field_state_sync( + eFieldStateSyncResult sync_result = simulation_nodes_field_state_sync( *input_node, node, field_state_by_socket_id); if (sync_result & eFieldStateSyncResult::CHANGED_A) { need_update = true; -- 2.30.2 From dfe18c5112e8766579dcce34661df829afdb5195 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Thu, 20 Apr 2023 11:28:40 +0200 Subject: [PATCH 10/26] Cleanup: Clang formatting --- .../blenkernel/BKE_simulation_state.hh | 19 +++++---------- .../intern/node_tree_field_inferencing.cc | 6 ++--- .../nodes/node_geo_simulation_input.cc | 10 +++----- .../nodes/node_geo_simulation_output.cc | 23 +++++++++++-------- 4 files changed, 24 insertions(+), 34 deletions(-) diff --git a/source/blender/blenkernel/BKE_simulation_state.hh b/source/blender/blenkernel/BKE_simulation_state.hh index 7082626ebac..d3e992a9a94 100644 --- a/source/blender/blenkernel/BKE_simulation_state.hh +++ b/source/blender/blenkernel/BKE_simulation_state.hh @@ -13,8 +13,7 @@ class SimulationStateItem { virtual ~SimulationStateItem() = default; }; -template -class TypedSimulationStateItem : public SimulationStateItem { +template class TypedSimulationStateItem : public SimulationStateItem { public: using DataType = T; @@ -31,8 +30,7 @@ class TypedSimulationStateItem : public SimulationStateItem { { data_ = std::move(data); } - template - TypedSimulationStateItem(const U &data) + template TypedSimulationStateItem(const U &data) { data_ = data; } @@ -40,8 +38,7 @@ class TypedSimulationStateItem : public SimulationStateItem { TypedSimulationStateItem(const TypedSimulationStateItem &) = delete; TypedSimulationStateItem(TypedSimulationStateItem &&) = delete; - virtual ~TypedSimulationStateItem() { - } + virtual ~TypedSimulationStateItem() {} TypedSimulationStateItem &operator=(const TypedSimulationStateItem &) = delete; TypedSimulationStateItem &operator=(TypedSimulationStateItem &&) = delete; @@ -53,8 +50,7 @@ class TypedSimulationStateItem : public SimulationStateItem { }; /** Specialization for GeometrySet which ensures the state owns the geometry data. */ -template <> -class TypedSimulationStateItem : public SimulationStateItem { +template<> class TypedSimulationStateItem : public SimulationStateItem { public: using DataType = GeometrySet; @@ -73,8 +69,7 @@ class TypedSimulationStateItem : public SimulationStateItem { data_ = std::move(data); data_.ensure_owns_direct_data(); } - template - TypedSimulationStateItem(const U &data) + template TypedSimulationStateItem(const U &data) { data_ = data; data_.ensure_owns_direct_data(); @@ -83,8 +78,7 @@ class TypedSimulationStateItem : public SimulationStateItem { TypedSimulationStateItem(const TypedSimulationStateItem &) = delete; TypedSimulationStateItem(TypedSimulationStateItem &&) = delete; - virtual ~TypedSimulationStateItem() { - } + virtual ~TypedSimulationStateItem() {} TypedSimulationStateItem &operator=(const TypedSimulationStateItem &) = delete; TypedSimulationStateItem &operator=(TypedSimulationStateItem &&) = delete; @@ -95,7 +89,6 @@ class TypedSimulationStateItem : public SimulationStateItem { } }; - class SimulationZoneState { public: Vector> items; diff --git a/source/blender/blenkernel/intern/node_tree_field_inferencing.cc b/source/blender/blenkernel/intern/node_tree_field_inferencing.cc index 911ce15a16a..1cecb74c07e 100644 --- a/source/blender/blenkernel/intern/node_tree_field_inferencing.cc +++ b/source/blender/blenkernel/intern/node_tree_field_inferencing.cc @@ -445,8 +445,7 @@ static void propagate_data_requirements_from_right_to_left( } /* Find reverse dependencies and resolve conflicts, which may require another pass. */ - if (propagate_special_data_requirements(tree, *node, field_state_by_socket_id)) - { + if (propagate_special_data_requirements(tree, *node, field_state_by_socket_id)) { need_update = true; } } @@ -577,8 +576,7 @@ static void propagate_field_status_from_left_to_right( } /* Find reverse dependencies and resolve conflicts, which may require another pass. */ - if (propagate_special_data_requirements(tree, *node, field_state_by_socket_id)) - { + if (propagate_special_data_requirements(tree, *node, field_state_by_socket_id)) { need_update = true; } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc b/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc index f78354af0c8..36de2b32f6e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc @@ -14,8 +14,7 @@ namespace blender::nodes { -template -static void copy_typed_initial_simulation_state(lf::Params ¶ms, int index) +template static void copy_typed_initial_simulation_state(lf::Params ¶ms, int index) { T *data = params.try_get_input_data_ptr_or_request(index); if (data != nullptr) { @@ -23,10 +22,7 @@ static void copy_typed_initial_simulation_state(lf::Params ¶ms, int index) } } -static void copy_initial_simulation_state( - lf::Params ¶ms, - int index, - short socket_type) +static void copy_initial_simulation_state(lf::Params ¶ms, int index, short socket_type) { switch (socket_type) { case SOCK_FLOAT: @@ -132,7 +128,7 @@ static void copy_next_simulation_state(lf::Params ¶ms, } } -} +} // namespace blender::nodes namespace blender::nodes::node_geo_simulation_input_cc { diff --git a/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc b/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc index 0c2ca55dd96..add271761cf 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc @@ -166,8 +166,9 @@ const CPPType &get_simulation_item_cpp_type(const NodeSimulationItem &item) } } -template -static std::unique_ptr> make_typed_simulation_state_item(lf::Params ¶ms, int index) +template +static std::unique_ptr> make_typed_simulation_state_item( + lf::Params ¶ms, int index) { using bke::sim::TypedSimulationStateItem; @@ -212,21 +213,23 @@ static std::unique_ptr make_simulation_state_item } } -template -static void copy_typed_simulation_state_output(lf::Params ¶ms, int index, const bke::sim::SimulationStateItem &state_item) +template +static void copy_typed_simulation_state_output(lf::Params ¶ms, + int index, + const bke::sim::SimulationStateItem &state_item) { using bke::sim::TypedSimulationStateItem; - if (auto *typed_state_item = dynamic_cast *>(&state_item)) { + if (auto *typed_state_item = dynamic_cast *>( + &state_item)) { params.set_output(index, typed_state_item->data()); } } -static void copy_simulation_state_output( - lf::Params ¶ms, - int index, - short socket_type, - const bke::sim::SimulationStateItem &state_item) +static void copy_simulation_state_output(lf::Params ¶ms, + int index, + short socket_type, + const bke::sim::SimulationStateItem &state_item) { switch (socket_type) { case SOCK_FLOAT: -- 2.30.2 From c528579b34a77ca054fafc0e9de842b18af07925 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Thu, 20 Apr 2023 11:37:49 +0200 Subject: [PATCH 11/26] Comment on index offset in simulation input node output parameters. --- source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc b/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc index 36de2b32f6e..c21f49ccd2c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc @@ -18,6 +18,7 @@ template static void copy_typed_initial_simulation_state(lf::Params { T *data = params.try_get_input_data_ptr_or_request(index); if (data != nullptr) { + /* First output parameter is "Delta Time", state item parameters start at index 1. */ params.set_output(index + 1, std::move(*data)); } } -- 2.30.2 From dd0fc76ba002551947c643c12e6c5475c70914cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Thu, 20 Apr 2023 11:54:45 +0200 Subject: [PATCH 12/26] Cleanup: Use enum types and const parameters where possible. --- .../nodes/node_geo_simulation_input.cc | 25 +++++++++++++------ .../nodes/node_geo_simulation_output.cc | 18 +++++++------ 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc b/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc index c21f49ccd2c..c0681a703f1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc @@ -14,7 +14,8 @@ namespace blender::nodes { -template static void copy_typed_initial_simulation_state(lf::Params ¶ms, int index) +template +static void copy_typed_initial_simulation_state(lf::Params ¶ms, const int index) { T *data = params.try_get_input_data_ptr_or_request(index); if (data != nullptr) { @@ -23,7 +24,9 @@ template static void copy_typed_initial_simulation_state(lf::Params } } -static void copy_initial_simulation_state(lf::Params ¶ms, int index, short socket_type) +static void copy_initial_simulation_state(lf::Params ¶ms, + const int index, + const eNodeSocketDatatype socket_type) { switch (socket_type) { case SOCK_FLOAT: @@ -71,18 +74,19 @@ static void copy_initial_simulation_state(lf::Params ¶ms, int index, short s template static void copy_typed_next_simulation_state(lf::Params ¶ms, - int index, + const int index, const bke::sim::SimulationStateItem &state_item) { - if (auto *typed_state_item = dynamic_cast *>( + if (const auto *typed_state_item = dynamic_cast *>( &state_item)) { + /* First output parameter is "Delta Time", state item parameters start at index 1. */ params.set_output(index + 1, typed_state_item->data()); } } static void copy_next_simulation_state(lf::Params ¶ms, - int index, - short socket_type, + const int index, + const eNodeSocketDatatype socket_type, const bke::sim::SimulationStateItem &state_item) { switch (socket_type) { @@ -182,7 +186,8 @@ class LazyFunctionForSimulationInputNode final : public LazyFunction { if (params.output_was_set(i + 1)) { continue; } - copy_initial_simulation_state(params, i, simulation_items_[i].socket_type); + copy_initial_simulation_state( + params, i, static_cast(simulation_items_[i].socket_type)); } } else { @@ -193,7 +198,11 @@ class LazyFunctionForSimulationInputNode final : public LazyFunction { } const bke::sim::SimulationStateItem *state_item = prev_zone_state->items[i].get(); if (state_item != nullptr) { - copy_next_simulation_state(params, i, simulation_items_[i].socket_type, *state_item); + copy_next_simulation_state( + params, + i, + static_cast(simulation_items_[i].socket_type), + *state_item); } } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc b/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc index add271761cf..52db6f6f61d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc @@ -18,7 +18,7 @@ namespace blender::nodes { static std::unique_ptr socket_declaration_for_simulation_item( - const NodeSimulationItem &item, eNodeSocketInOut in_out, int index) + const NodeSimulationItem &item, const eNodeSocketInOut in_out, const int index) { std::unique_ptr decl; switch (eNodeSocketDatatype(item.socket_type)) { @@ -168,7 +168,7 @@ const CPPType &get_simulation_item_cpp_type(const NodeSimulationItem &item) template static std::unique_ptr> make_typed_simulation_state_item( - lf::Params ¶ms, int index) + lf::Params ¶ms, const int index) { using bke::sim::TypedSimulationStateItem; @@ -180,7 +180,7 @@ static std::unique_ptr> make_typed_simulat } static std::unique_ptr make_simulation_state_item( - lf::Params ¶ms, int index, short socket_type) + lf::Params ¶ms, const int index, const eNodeSocketDatatype socket_type) { switch (socket_type) { case SOCK_FLOAT: @@ -215,7 +215,7 @@ static std::unique_ptr make_simulation_state_item template static void copy_typed_simulation_state_output(lf::Params ¶ms, - int index, + const int index, const bke::sim::SimulationStateItem &state_item) { using bke::sim::TypedSimulationStateItem; @@ -227,8 +227,8 @@ static void copy_typed_simulation_state_output(lf::Params ¶ms, } static void copy_simulation_state_output(lf::Params ¶ms, - int index, - short socket_type, + const int index, + const eNodeSocketDatatype socket_type, const bke::sim::SimulationStateItem &state_item) { switch (socket_type) { @@ -352,7 +352,8 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction { continue; } - new_zone_state.items[i] = make_simulation_state_item(params, i, item.socket_type); + new_zone_state.items[i] = make_simulation_state_item( + params, i, static_cast(item.socket_type)); } if (all_available) { @@ -372,7 +373,8 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction { if (state_item == nullptr) { continue; } - copy_simulation_state_output(params, i, item.socket_type, *state_item); + copy_simulation_state_output( + params, i, static_cast(item.socket_type), *state_item); } params.set_default_remaining_outputs(); } -- 2.30.2 From 3184d3499bca9f327ccb8a7b8b0ca9ac91976640 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Thu, 20 Apr 2023 12:13:51 +0200 Subject: [PATCH 13/26] Use bNodeSocketType to get CPPTypes for simulation items. --- .../nodes/node_geo_simulation_output.cc | 34 +++---------------- 1 file changed, 5 insertions(+), 29 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc b/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc index 52db6f6f61d..4564ee34a94 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc @@ -135,35 +135,11 @@ NodeSimulationItem *simulation_item_add_from_socket(NodeGeometrySimulationOutput const CPPType &get_simulation_item_cpp_type(const NodeSimulationItem &item) { - switch (item.socket_type) { - case SOCK_FLOAT: - return CPPType::get>(); - case SOCK_VECTOR: - return CPPType::get>(); - case SOCK_RGBA: - return CPPType::get>(); - case SOCK_BOOLEAN: - return CPPType::get>(); - case SOCK_INT: - return CPPType::get>(); - case SOCK_STRING: - return CPPType::get>(); - case SOCK_OBJECT: - return CPPType::get(); - case SOCK_GEOMETRY: - return CPPType::get(); - case SOCK_COLLECTION: - return CPPType::get(); - case SOCK_TEXTURE: - return CPPType::get(); - case SOCK_IMAGE: - return CPPType::get(); - case SOCK_MATERIAL: - return CPPType::get(); - default: - BLI_assert_unreachable(); - return CPPType::get(); - } + const char *socket_idname = nodeStaticSocketType(item.socket_type, 0); + const bNodeSocketType *typeinfo = nodeSocketTypeFind(socket_idname); + BLI_assert(typeinfo); + BLI_assert(typeinfo->geometry_nodes_cpp_type); + return *typeinfo->geometry_nodes_cpp_type; } template -- 2.30.2 From e904bac1cad3276d24d4d3dc8e99ce775a570bf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Thu, 20 Apr 2023 12:28:15 +0200 Subject: [PATCH 14/26] Remove unused comparison operators for SocketFieldState. --- .../blenkernel/intern/node_tree_field_inferencing.cc | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/source/blender/blenkernel/intern/node_tree_field_inferencing.cc b/source/blender/blenkernel/intern/node_tree_field_inferencing.cc index 1cecb74c07e..3a92d642a71 100644 --- a/source/blender/blenkernel/intern/node_tree_field_inferencing.cc +++ b/source/blender/blenkernel/intern/node_tree_field_inferencing.cc @@ -156,18 +156,6 @@ struct SocketFieldState { * supports this socket to be a single value, or because a node afterwards requires this to be a * single value. */ bool requires_single = false; - - bool operator==(const SocketFieldState &other) - { - return this->is_field_source == other.is_field_source && - this->is_always_single == other.is_always_single && - this->is_single == other.is_single && this->requires_single == other.requires_single; - } - - bool operator!=(const SocketFieldState &other) - { - return !(*this == other); - } }; static Vector gather_input_socket_dependencies( -- 2.30.2 From d17ad3f7fbc8c432b3443db497f98b43ce2e562a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Thu, 20 Apr 2023 13:04:09 +0200 Subject: [PATCH 15/26] WIP: use templated item class only for simple data types. --- .../blenkernel/BKE_simulation_state.hh | 63 ++++--------------- .../nodes/node_geo_simulation_output.cc | 47 +++++++------- 2 files changed, 37 insertions(+), 73 deletions(-) diff --git a/source/blender/blenkernel/BKE_simulation_state.hh b/source/blender/blenkernel/BKE_simulation_state.hh index d3e992a9a94..b595464a95b 100644 --- a/source/blender/blenkernel/BKE_simulation_state.hh +++ b/source/blender/blenkernel/BKE_simulation_state.hh @@ -8,12 +8,12 @@ namespace blender::bke::sim { -class SimulationStateItem { +class SimulationStateItem : public NonCopyable, public NonMovable { public: virtual ~SimulationStateItem() = default; }; -template class TypedSimulationStateItem : public SimulationStateItem { +template class DataSimulationStateItem : public SimulationStateItem { public: using DataType = T; @@ -21,71 +21,32 @@ template class TypedSimulationStateItem : public SimulationStateItem T data_; public: - TypedSimulationStateItem() = default; - TypedSimulationStateItem(const T &data) + DataSimulationStateItem() = default; + DataSimulationStateItem(DataType data) { data_ = std::move(data); } - TypedSimulationStateItem(T &&data) - { - data_ = std::move(data); - } - template TypedSimulationStateItem(const U &data) - { - data_ = data; - } - TypedSimulationStateItem(const TypedSimulationStateItem &) = delete; - TypedSimulationStateItem(TypedSimulationStateItem &&) = delete; - - virtual ~TypedSimulationStateItem() {} - - TypedSimulationStateItem &operator=(const TypedSimulationStateItem &) = delete; - TypedSimulationStateItem &operator=(TypedSimulationStateItem &&) = delete; - - const T &data() const + const DataType &data() const { return data_; } }; -/** Specialization for GeometrySet which ensures the state owns the geometry data. */ -template<> class TypedSimulationStateItem : public SimulationStateItem { - public: - using DataType = GeometrySet; - +class GeometrySimulationStateItem : public SimulationStateItem { private: - GeometrySet data_; + GeometrySet geometry_; public: - TypedSimulationStateItem() = default; - TypedSimulationStateItem(const GeometrySet &data) + GeometrySimulationStateItem() = default; + GeometrySimulationStateItem(GeometrySet geometry) { - data_ = std::move(data); - data_.ensure_owns_direct_data(); - } - TypedSimulationStateItem(GeometrySet &&data) - { - data_ = std::move(data); - data_.ensure_owns_direct_data(); - } - template TypedSimulationStateItem(const U &data) - { - data_ = data; - data_.ensure_owns_direct_data(); + geometry_ = std::move(geometry); } - TypedSimulationStateItem(const TypedSimulationStateItem &) = delete; - TypedSimulationStateItem(TypedSimulationStateItem &&) = delete; - - virtual ~TypedSimulationStateItem() {} - - TypedSimulationStateItem &operator=(const TypedSimulationStateItem &) = delete; - TypedSimulationStateItem &operator=(TypedSimulationStateItem &&) = delete; - - const GeometrySet &data() const + const GeometrySet &geometry() const { - return data_; + return geometry_; } }; diff --git a/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc b/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc index 4564ee34a94..1b29b116e85 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc @@ -143,16 +143,29 @@ const CPPType &get_simulation_item_cpp_type(const NodeSimulationItem &item) } template -static std::unique_ptr> make_typed_simulation_state_item( +static std::unique_ptr make_data_simulation_state_item( lf::Params ¶ms, const int index) { - using bke::sim::TypedSimulationStateItem; + using bke::sim::DataSimulationStateItem; if (const T *data = params.try_get_input_data_ptr_or_request(index)) { - return std::make_unique>(*data); + return std::make_unique>(*data); } - return std::make_unique>(); + return nullptr; +} + +static std::unique_ptr make_geometry_simulation_state_item( + lf::Params ¶ms, const int index) +{ + using bke::sim::GeometrySimulationStateItem; + + if (GeometrySet *data = params.try_get_input_data_ptr_or_request(index)) { + data->ensure_owns_direct_data(); + return std::make_unique(*data); + } + + return nullptr; } static std::unique_ptr make_simulation_state_item( @@ -160,32 +173,22 @@ static std::unique_ptr make_simulation_state_item { switch (socket_type) { case SOCK_FLOAT: - return make_typed_simulation_state_item>(params, index); + return make_data_simulation_state_item>(params, index); case SOCK_VECTOR: - return make_typed_simulation_state_item>(params, index); + return make_data_simulation_state_item>(params, index); case SOCK_RGBA: - return make_typed_simulation_state_item>(params, index); + return make_data_simulation_state_item>(params, index); case SOCK_BOOLEAN: - return make_typed_simulation_state_item>(params, index); + return make_data_simulation_state_item>(params, index); case SOCK_INT: - return make_typed_simulation_state_item>(params, index); + return make_data_simulation_state_item>(params, index); case SOCK_STRING: - return make_typed_simulation_state_item>(params, index); - case SOCK_OBJECT: - return make_typed_simulation_state_item(params, index); + return make_data_simulation_state_item>(params, index); case SOCK_GEOMETRY: - return make_typed_simulation_state_item(params, index); - case SOCK_COLLECTION: - return make_typed_simulation_state_item(params, index); - case SOCK_TEXTURE: - return make_typed_simulation_state_item(params, index); - case SOCK_IMAGE: - return make_typed_simulation_state_item(params, index); - case SOCK_MATERIAL: - return make_typed_simulation_state_item(params, index); + return make_geometry_simulation_state_item(params, index); default: BLI_assert_unreachable(); - return make_typed_simulation_state_item(params, index); + return nullptr; } } -- 2.30.2 From 5eeef12f961fda94196ae45a3b415cb94f549662 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Thu, 20 Apr 2023 14:36:56 +0200 Subject: [PATCH 16/26] Reverted SimulationStateItem changes and templated copy functions. --- .../blenkernel/BKE_simulation_state.hh | 28 +---- .../nodes/geometry/node_geometry_util.hh | 1 + .../nodes/node_geo_simulation_input.cc | 118 +++--------------- .../nodes/node_geo_simulation_output.cc | 114 +++-------------- 4 files changed, 35 insertions(+), 226 deletions(-) diff --git a/source/blender/blenkernel/BKE_simulation_state.hh b/source/blender/blenkernel/BKE_simulation_state.hh index b595464a95b..45a8069f678 100644 --- a/source/blender/blenkernel/BKE_simulation_state.hh +++ b/source/blender/blenkernel/BKE_simulation_state.hh @@ -8,41 +8,17 @@ namespace blender::bke::sim { -class SimulationStateItem : public NonCopyable, public NonMovable { +class SimulationStateItem { public: virtual ~SimulationStateItem() = default; }; -template class DataSimulationStateItem : public SimulationStateItem { - public: - using DataType = T; - - private: - T data_; - - public: - DataSimulationStateItem() = default; - DataSimulationStateItem(DataType data) - { - data_ = std::move(data); - } - - const DataType &data() const - { - return data_; - } -}; - class GeometrySimulationStateItem : public SimulationStateItem { private: GeometrySet geometry_; public: - GeometrySimulationStateItem() = default; - GeometrySimulationStateItem(GeometrySet geometry) - { - geometry_ = std::move(geometry); - } + GeometrySimulationStateItem(GeometrySet geometry) : geometry_(std::move(geometry)) {} const GeometrySet &geometry() const { diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index 2dae1adc6be..e50409b2b1f 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -135,6 +135,7 @@ class FieldAtIndexInput final : public bke::GeometryFieldInput { void socket_declarations_for_simulation_items(Span items, NodeDeclaration &r_declaration); +const CPPType &get_simulation_item_cpp_type(eNodeSocketDatatype socket_type); const CPPType &get_simulation_item_cpp_type(const NodeSimulationItem &item); /** \warning Return value will be reallocated when items are added or removed. */ NodeSimulationItem *simulation_item_add_from_socket(NodeGeometrySimulationOutput &storage, diff --git a/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc b/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc index c0681a703f1..6689ff6c693 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc @@ -14,73 +14,18 @@ namespace blender::nodes { -template -static void copy_typed_initial_simulation_state(lf::Params ¶ms, const int index) -{ - T *data = params.try_get_input_data_ptr_or_request(index); - if (data != nullptr) { - /* First output parameter is "Delta Time", state item parameters start at index 1. */ - params.set_output(index + 1, std::move(*data)); - } -} - static void copy_initial_simulation_state(lf::Params ¶ms, const int index, const eNodeSocketDatatype socket_type) { - switch (socket_type) { - case SOCK_FLOAT: - copy_typed_initial_simulation_state>(params, index); - break; - case SOCK_VECTOR: - copy_typed_initial_simulation_state>(params, index); - break; - case SOCK_RGBA: - copy_typed_initial_simulation_state>(params, index); - break; - case SOCK_BOOLEAN: - copy_typed_initial_simulation_state>(params, index); - break; - case SOCK_INT: - copy_typed_initial_simulation_state>(params, index); - break; - case SOCK_STRING: - copy_typed_initial_simulation_state>(params, index); - break; - case SOCK_OBJECT: - copy_typed_initial_simulation_state(params, index); - break; - case SOCK_GEOMETRY: - copy_typed_initial_simulation_state(params, index); - break; - case SOCK_COLLECTION: - copy_typed_initial_simulation_state(params, index); - break; - case SOCK_TEXTURE: - copy_typed_initial_simulation_state(params, index); - break; - case SOCK_IMAGE: - copy_typed_initial_simulation_state(params, index); - break; - case SOCK_MATERIAL: - copy_typed_initial_simulation_state(params, index); - break; - default: - BLI_assert_unreachable(); - copy_typed_initial_simulation_state(params, index); - break; - } -} + const CPPType &cpptype = get_simulation_item_cpp_type(socket_type); -template -static void copy_typed_next_simulation_state(lf::Params ¶ms, - const int index, - const bke::sim::SimulationStateItem &state_item) -{ - if (const auto *typed_state_item = dynamic_cast *>( - &state_item)) { + void *src = params.try_get_input_data_ptr_or_request(index); + if (src != nullptr) { /* First output parameter is "Delta Time", state item parameters start at index 1. */ - params.set_output(index + 1, typed_state_item->data()); + void *dst = params.get_output_data_ptr(index + 1); + cpptype.move_construct(src, dst); + params.output_set(index + 1); } } @@ -89,47 +34,16 @@ static void copy_next_simulation_state(lf::Params ¶ms, const eNodeSocketDatatype socket_type, const bke::sim::SimulationStateItem &state_item) { - switch (socket_type) { - case SOCK_FLOAT: - copy_typed_next_simulation_state>(params, index, state_item); - break; - case SOCK_VECTOR: - copy_typed_next_simulation_state>(params, index, state_item); - break; - case SOCK_RGBA: - copy_typed_next_simulation_state>(params, index, state_item); - break; - case SOCK_BOOLEAN: - copy_typed_next_simulation_state>(params, index, state_item); - break; - case SOCK_INT: - copy_typed_next_simulation_state>(params, index, state_item); - break; - case SOCK_STRING: - copy_typed_next_simulation_state>(params, index, state_item); - break; - case SOCK_OBJECT: - copy_typed_next_simulation_state(params, index, state_item); - break; - case SOCK_GEOMETRY: - copy_typed_next_simulation_state(params, index, state_item); - break; - case SOCK_COLLECTION: - copy_typed_next_simulation_state(params, index, state_item); - break; - case SOCK_TEXTURE: - copy_typed_next_simulation_state(params, index, state_item); - break; - case SOCK_IMAGE: - copy_typed_next_simulation_state(params, index, state_item); - break; - case SOCK_MATERIAL: - copy_typed_next_simulation_state(params, index, state_item); - break; - default: - BLI_assert_unreachable(); - copy_typed_next_simulation_state(params, index, state_item); - break; + const CPPType &cpptype = get_simulation_item_cpp_type(socket_type); + + /* TODO */ + UNUSED_VARS(state_item); +// if (const void *src = state_item.data()) { + if (const void *src = cpptype.default_value()) { + /* First output parameter is "Delta Time", state item parameters start at index 1. */ + void *dst = params.get_output_data_ptr(index + 1); + cpptype.copy_construct(src, dst); + params.output_set(index + 1); } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc b/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc index 1b29b116e85..7659b3467a2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc @@ -133,76 +133,27 @@ NodeSimulationItem *simulation_item_add_from_socket(NodeGeometrySimulationOutput return &added_item; } -const CPPType &get_simulation_item_cpp_type(const NodeSimulationItem &item) +const CPPType &get_simulation_item_cpp_type(const eNodeSocketDatatype socket_type) { - const char *socket_idname = nodeStaticSocketType(item.socket_type, 0); + const char *socket_idname = nodeStaticSocketType(socket_type, 0); const bNodeSocketType *typeinfo = nodeSocketTypeFind(socket_idname); BLI_assert(typeinfo); BLI_assert(typeinfo->geometry_nodes_cpp_type); return *typeinfo->geometry_nodes_cpp_type; } -template -static std::unique_ptr make_data_simulation_state_item( - lf::Params ¶ms, const int index) +const CPPType &get_simulation_item_cpp_type(const NodeSimulationItem &item) { - using bke::sim::DataSimulationStateItem; - - if (const T *data = params.try_get_input_data_ptr_or_request(index)) { - return std::make_unique>(*data); - } - - return nullptr; -} - -static std::unique_ptr make_geometry_simulation_state_item( - lf::Params ¶ms, const int index) -{ - using bke::sim::GeometrySimulationStateItem; - - if (GeometrySet *data = params.try_get_input_data_ptr_or_request(index)) { - data->ensure_owns_direct_data(); - return std::make_unique(*data); - } - - return nullptr; + return get_simulation_item_cpp_type(static_cast(item.socket_type)); } static std::unique_ptr make_simulation_state_item( lf::Params ¶ms, const int index, const eNodeSocketDatatype socket_type) { - switch (socket_type) { - case SOCK_FLOAT: - return make_data_simulation_state_item>(params, index); - case SOCK_VECTOR: - return make_data_simulation_state_item>(params, index); - case SOCK_RGBA: - return make_data_simulation_state_item>(params, index); - case SOCK_BOOLEAN: - return make_data_simulation_state_item>(params, index); - case SOCK_INT: - return make_data_simulation_state_item>(params, index); - case SOCK_STRING: - return make_data_simulation_state_item>(params, index); - case SOCK_GEOMETRY: - return make_geometry_simulation_state_item(params, index); - default: - BLI_assert_unreachable(); - return nullptr; - } -} - -template -static void copy_typed_simulation_state_output(lf::Params ¶ms, - const int index, - const bke::sim::SimulationStateItem &state_item) -{ - using bke::sim::TypedSimulationStateItem; - - if (auto *typed_state_item = dynamic_cast *>( - &state_item)) { - params.set_output(index, typed_state_item->data()); - } + /* TODO */ + UNUSED_VARS(params, index, socket_type); + GeometrySet geometry; + return std::make_unique(geometry); } static void copy_simulation_state_output(lf::Params ¶ms, @@ -210,47 +161,14 @@ static void copy_simulation_state_output(lf::Params ¶ms, const eNodeSocketDatatype socket_type, const bke::sim::SimulationStateItem &state_item) { - switch (socket_type) { - case SOCK_FLOAT: - copy_typed_simulation_state_output>(params, index, state_item); - break; - case SOCK_VECTOR: - copy_typed_simulation_state_output>(params, index, state_item); - break; - case SOCK_RGBA: - copy_typed_simulation_state_output>(params, index, state_item); - break; - case SOCK_BOOLEAN: - copy_typed_simulation_state_output>(params, index, state_item); - break; - case SOCK_INT: - copy_typed_simulation_state_output>(params, index, state_item); - break; - case SOCK_STRING: - copy_typed_simulation_state_output>(params, index, state_item); - break; - case SOCK_OBJECT: - copy_typed_simulation_state_output(params, index, state_item); - break; - case SOCK_GEOMETRY: - copy_typed_simulation_state_output(params, index, state_item); - break; - case SOCK_COLLECTION: - copy_typed_simulation_state_output(params, index, state_item); - break; - case SOCK_TEXTURE: - copy_typed_simulation_state_output(params, index, state_item); - break; - case SOCK_IMAGE: - copy_typed_simulation_state_output(params, index, state_item); - break; - case SOCK_MATERIAL: - copy_typed_simulation_state_output(params, index, state_item); - break; - default: - BLI_assert_unreachable(); - copy_typed_simulation_state_output(params, index, state_item); - break; + const CPPType &cpptype = get_simulation_item_cpp_type(socket_type); + + /* TODO */ + UNUSED_VARS(params, index, socket_type, state_item); + if (const void *src = cpptype.default_value()) { + void *dst = params.get_output_data_ptr(index); + cpptype.copy_construct(src, dst); + params.output_set(index); } } -- 2.30.2 From 9f0267d2158384539bfc706908866db10dee8e46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Thu, 20 Apr 2023 14:48:52 +0200 Subject: [PATCH 17/26] Use direct accessor method to get sockets. --- .../intern/node_tree_field_inferencing.cc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/source/blender/blenkernel/intern/node_tree_field_inferencing.cc b/source/blender/blenkernel/intern/node_tree_field_inferencing.cc index 3a92d642a71..d83b47c951c 100644 --- a/source/blender/blenkernel/intern/node_tree_field_inferencing.cc +++ b/source/blender/blenkernel/intern/node_tree_field_inferencing.cc @@ -300,18 +300,18 @@ static eFieldStateSyncResult simulation_nodes_field_state_sync( { eFieldStateSyncResult res = static_cast(0); for (const int i : output_node.input_sockets().index_range()) { - const bNodeSocket *input_socket = input_node.input_sockets()[i]; - const bNodeSocket *output_socket = output_node.input_sockets()[i]; - SocketFieldState &input_state = field_state_by_socket_id[input_socket->index_in_tree()]; - SocketFieldState &output_state = field_state_by_socket_id[output_socket->index_in_tree()]; + const bNodeSocket &input_socket = input_node.input_socket(i); + const bNodeSocket &output_socket = output_node.input_socket(i); + SocketFieldState &input_state = field_state_by_socket_id[input_socket.index_in_tree()]; + SocketFieldState &output_state = field_state_by_socket_id[output_socket.index_in_tree()]; res |= sync_field_states(input_state, output_state); } for (const int i : output_node.output_sockets().index_range()) { /* First input node output is Delta Time which does not appear in the output node outputs. */ - const bNodeSocket *input_socket = input_node.output_sockets()[i + 1]; - const bNodeSocket *output_socket = output_node.output_sockets()[i]; - SocketFieldState &input_state = field_state_by_socket_id[input_socket->index_in_tree()]; - SocketFieldState &output_state = field_state_by_socket_id[output_socket->index_in_tree()]; + const bNodeSocket &input_socket = input_node.output_socket(i + 1); + const bNodeSocket &output_socket = output_node.output_socket(i); + SocketFieldState &input_state = field_state_by_socket_id[input_socket.index_in_tree()]; + SocketFieldState &output_state = field_state_by_socket_id[output_socket.index_in_tree()]; res |= sync_field_states(input_state, output_state); } return res; -- 2.30.2 From 38eb1b7a7f1b512ed47a083f8f461996281d87a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Thu, 20 Apr 2023 15:39:11 +0200 Subject: [PATCH 18/26] Limit the supported socket types to Geometry and basic data (PODs). --- .../nodes/geometry/node_geometry_util.hh | 2 ++ .../nodes/node_geo_simulation_output.cc | 34 +++++++++++-------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index e50409b2b1f..cd21ec186bd 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -133,6 +133,8 @@ class FieldAtIndexInput final : public bke::GeometryFieldInput { } }; +bool is_simulation_item_type_supported(eNodeSocketDatatype socket_type); + void socket_declarations_for_simulation_items(Span items, NodeDeclaration &r_declaration); const CPPType &get_simulation_item_cpp_type(eNodeSocketDatatype socket_type); diff --git a/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc b/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc index 7659b3467a2..05df91cdd0a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc @@ -17,9 +17,24 @@ namespace blender::nodes { +bool is_simulation_item_type_supported(const eNodeSocketDatatype socket_type) +{ + return ELEM(socket_type, + SOCK_FLOAT, + SOCK_VECTOR, + SOCK_RGBA, + SOCK_BOOLEAN, + SOCK_INT, + SOCK_STRING, + SOCK_GEOMETRY); +} + static std::unique_ptr socket_declaration_for_simulation_item( const NodeSimulationItem &item, const eNodeSocketInOut in_out, const int index) { + BLI_assert( + is_simulation_item_type_supported(static_cast(item.socket_type))); + std::unique_ptr decl; switch (eNodeSocketDatatype(item.socket_type)) { case SOCK_FLOAT: @@ -50,24 +65,9 @@ static std::unique_ptr socket_declaration_for_simulation_item case SOCK_STRING: decl = std::make_unique(); break; - case SOCK_OBJECT: - decl = std::make_unique(); - break; case SOCK_GEOMETRY: decl = std::make_unique(); break; - case SOCK_COLLECTION: - decl = std::make_unique(); - break; - case SOCK_TEXTURE: - decl = std::make_unique(); - break; - case SOCK_IMAGE: - decl = std::make_unique(); - break; - case SOCK_MATERIAL: - decl = std::make_unique(); - break; default: BLI_assert_unreachable(); } @@ -108,6 +108,10 @@ static bool simulation_items_unique_name_check(void *arg, const char *name) NodeSimulationItem *simulation_item_add_from_socket(NodeGeometrySimulationOutput &storage, const bNodeSocket &socket) { + if (!is_simulation_item_type_supported(static_cast(socket.type))) { + return nullptr; + } + char unique_name[MAX_NAME + 4] = ""; BLI_uniquename_cb(simulation_items_unique_name_check, &storage, -- 2.30.2 From 9a4a331df99d1c84a46ceb3306b79be6ec0f89dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Thu, 20 Apr 2023 17:47:21 +0200 Subject: [PATCH 19/26] Restore default behavior for GeometrySet simulation state items. --- .../nodes/node_geo_simulation_input.cc | 19 +++++++---- .../nodes/node_geo_simulation_output.cc | 34 +++++++++++++------ 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc b/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc index 6689ff6c693..07f72711c62 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc @@ -37,13 +37,20 @@ static void copy_next_simulation_state(lf::Params ¶ms, const CPPType &cpptype = get_simulation_item_cpp_type(socket_type); /* TODO */ - UNUSED_VARS(state_item); -// if (const void *src = state_item.data()) { - if (const void *src = cpptype.default_value()) { + if (socket_type == SOCK_GEOMETRY) { + const bke::sim::GeometrySimulationStateItem &geo_state_item = static_cast(state_item); + const GeometrySet &src = geo_state_item.geometry(); /* First output parameter is "Delta Time", state item parameters start at index 1. */ - void *dst = params.get_output_data_ptr(index + 1); - cpptype.copy_construct(src, dst); - params.output_set(index + 1); + params.set_output(index + 1, src); + } + else { + // if (const void *src = state_item.data()) { + if (const void *src = cpptype.default_value()) { + /* First output parameter is "Delta Time", state item parameters start at index 1. */ + void *dst = params.get_output_data_ptr(index + 1); + cpptype.copy_construct(src, dst); + params.output_set(index + 1); + } } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc b/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc index 05df91cdd0a..df43f1f15b6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc @@ -152,12 +152,18 @@ const CPPType &get_simulation_item_cpp_type(const NodeSimulationItem &item) } static std::unique_ptr make_simulation_state_item( - lf::Params ¶ms, const int index, const eNodeSocketDatatype socket_type) + const eNodeSocketDatatype socket_type, void *input_data) { /* TODO */ - UNUSED_VARS(params, index, socket_type); - GeometrySet geometry; - return std::make_unique(geometry); + if (socket_type == SOCK_GEOMETRY) { + GeometrySet *input_geometry = static_cast(input_data); + input_geometry->ensure_owns_direct_data(); + return std::make_unique(*input_geometry); + } + else { + GeometrySet geometry; + return std::make_unique(geometry); + } } static void copy_simulation_state_output(lf::Params ¶ms, @@ -167,12 +173,18 @@ static void copy_simulation_state_output(lf::Params ¶ms, { const CPPType &cpptype = get_simulation_item_cpp_type(socket_type); - /* TODO */ - UNUSED_VARS(params, index, socket_type, state_item); - if (const void *src = cpptype.default_value()) { - void *dst = params.get_output_data_ptr(index); - cpptype.copy_construct(src, dst); - params.output_set(index); + if (socket_type == SOCK_GEOMETRY) { + const bke::sim::GeometrySimulationStateItem &geo_state_item = + static_cast(state_item); + params.set_output(index, geo_state_item.geometry()); + } + else { + /* TODO */ + if (const void *src = cpptype.default_value()) { + void *dst = params.get_output_data_ptr(index); + cpptype.copy_construct(src, dst); + params.output_set(index); + } } } @@ -254,7 +266,7 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction { } new_zone_state.items[i] = make_simulation_state_item( - params, i, static_cast(item.socket_type)); + static_cast(item.socket_type), input_data); } if (all_available) { -- 2.30.2 From 7d7b6318633759e280f683c1d6dba35fdb95f4ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Thu, 20 Apr 2023 18:03:33 +0200 Subject: [PATCH 20/26] Fix propagation of the `is_single` field state. --- .../blenkernel/intern/node_tree_field_inferencing.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/node_tree_field_inferencing.cc b/source/blender/blenkernel/intern/node_tree_field_inferencing.cc index d83b47c951c..e18b4a251e9 100644 --- a/source/blender/blenkernel/intern/node_tree_field_inferencing.cc +++ b/source/blender/blenkernel/intern/node_tree_field_inferencing.cc @@ -273,17 +273,20 @@ ENUM_OPERATORS(eFieldStateSyncResult, CHANGED_B) static eFieldStateSyncResult sync_field_states(SocketFieldState &a, SocketFieldState &b) { const bool requires_single = a.requires_single || b.requires_single; + const bool is_single = a.is_single && b.is_single; eFieldStateSyncResult res = static_cast(0); - if (a.requires_single != requires_single) { + if (a.requires_single != requires_single || a.is_single != is_single) { res |= eFieldStateSyncResult::CHANGED_A; } - if (b.requires_single != requires_single) { + if (b.requires_single != requires_single || b.is_single != is_single) { res |= eFieldStateSyncResult::CHANGED_B; } a.requires_single = requires_single; b.requires_single = requires_single; + a.is_single = is_single; + b.is_single = is_single; return res; } -- 2.30.2 From d3a8401d7b2bf556f2b61ad121e8811c2d4025a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Fri, 21 Apr 2023 10:10:22 +0200 Subject: [PATCH 21/26] Only sync field state for the output sockets. --- .../blenkernel/intern/node_tree_field_inferencing.cc | 7 ------- 1 file changed, 7 deletions(-) diff --git a/source/blender/blenkernel/intern/node_tree_field_inferencing.cc b/source/blender/blenkernel/intern/node_tree_field_inferencing.cc index e18b4a251e9..1d563777e4a 100644 --- a/source/blender/blenkernel/intern/node_tree_field_inferencing.cc +++ b/source/blender/blenkernel/intern/node_tree_field_inferencing.cc @@ -302,13 +302,6 @@ static eFieldStateSyncResult simulation_nodes_field_state_sync( const MutableSpan field_state_by_socket_id) { eFieldStateSyncResult res = static_cast(0); - for (const int i : output_node.input_sockets().index_range()) { - const bNodeSocket &input_socket = input_node.input_socket(i); - const bNodeSocket &output_socket = output_node.input_socket(i); - SocketFieldState &input_state = field_state_by_socket_id[input_socket.index_in_tree()]; - SocketFieldState &output_state = field_state_by_socket_id[output_socket.index_in_tree()]; - res |= sync_field_states(input_state, output_state); - } for (const int i : output_node.output_sockets().index_range()) { /* First input node output is Delta Time which does not appear in the output node outputs. */ const bNodeSocket &input_socket = input_node.output_socket(i + 1); -- 2.30.2 From 4dea23a734ad221b855349bc82681bac33eac07d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Fri, 21 Apr 2023 12:08:42 +0200 Subject: [PATCH 22/26] Cleanup: Use enum class and a NONE item for the eFieldStateSyncResult. --- .../intern/node_tree_field_inferencing.cc | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/source/blender/blenkernel/intern/node_tree_field_inferencing.cc b/source/blender/blenkernel/intern/node_tree_field_inferencing.cc index 1d563777e4a..c1f858eb5fc 100644 --- a/source/blender/blenkernel/intern/node_tree_field_inferencing.cc +++ b/source/blender/blenkernel/intern/node_tree_field_inferencing.cc @@ -257,13 +257,15 @@ static OutputFieldDependency find_group_output_dependencies( } /** Result of syncing two field states. */ -enum eFieldStateSyncResult { +enum class eFieldStateSyncResult : char { + /* Nothing changed. */ + NONE = 0, /* State A has been modified. */ CHANGED_A = (1 << 0), /* State B has been modified. */ CHANGED_B = (1 << 1), }; -ENUM_OPERATORS(eFieldStateSyncResult, CHANGED_B) +ENUM_OPERATORS(eFieldStateSyncResult, eFieldStateSyncResult::CHANGED_B) /** * Compare both field states and select the most compatible. @@ -275,7 +277,7 @@ static eFieldStateSyncResult sync_field_states(SocketFieldState &a, SocketFieldS const bool requires_single = a.requires_single || b.requires_single; const bool is_single = a.is_single && b.is_single; - eFieldStateSyncResult res = static_cast(0); + eFieldStateSyncResult res = eFieldStateSyncResult::NONE; if (a.requires_single != requires_single || a.is_single != is_single) { res |= eFieldStateSyncResult::CHANGED_A; } @@ -301,7 +303,7 @@ static eFieldStateSyncResult simulation_nodes_field_state_sync( const bNode &output_node, const MutableSpan field_state_by_socket_id) { - eFieldStateSyncResult res = static_cast(0); + eFieldStateSyncResult res = eFieldStateSyncResult::NONE; for (const int i : output_node.output_sockets().index_range()) { /* First input node output is Delta Time which does not appear in the output node outputs. */ const bNodeSocket &input_socket = input_node.output_socket(i + 1); @@ -329,7 +331,7 @@ static bool propagate_special_data_requirements( if (const bNode *output_node = tree.node_by_id(data.output_node_id)) { eFieldStateSyncResult sync_result = simulation_nodes_field_state_sync( node, *output_node, field_state_by_socket_id); - if (sync_result & eFieldStateSyncResult::CHANGED_B) { + if ((bool)(sync_result & eFieldStateSyncResult::CHANGED_B)) { need_update = true; } } @@ -341,7 +343,7 @@ static bool propagate_special_data_requirements( if (node.identifier == data.output_node_id) { eFieldStateSyncResult sync_result = simulation_nodes_field_state_sync( *input_node, node, field_state_by_socket_id); - if (sync_result & eFieldStateSyncResult::CHANGED_A) { + if ((bool)(sync_result & eFieldStateSyncResult::CHANGED_A)) { need_update = true; } } -- 2.30.2 From ce48300fc61a9d98583b0d6ed84374c65654ce86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Fri, 21 Apr 2023 12:19:35 +0200 Subject: [PATCH 23/26] Extend comment for multi-pass field type propagation. --- .../blender/blenkernel/intern/node_tree_field_inferencing.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/node_tree_field_inferencing.cc b/source/blender/blenkernel/intern/node_tree_field_inferencing.cc index c1f858eb5fc..687286ae239 100644 --- a/source/blender/blenkernel/intern/node_tree_field_inferencing.cc +++ b/source/blender/blenkernel/intern/node_tree_field_inferencing.cc @@ -361,7 +361,8 @@ static void propagate_data_requirements_from_right_to_left( const Span toposort_result = tree.toposort_right_to_left(); while (true) { - /* Node updates may require sevaral passes due to cyclic dependencies. */ + /* Node updates may require sevaral passes due to cyclic dependencies caused by simulation + * input/output nodes. */ bool need_update = false; for (const bNode *node : toposort_result) { -- 2.30.2 From 75eea532d00ab43c8ab746aaf1b9e7ccbd150b70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Fri, 21 Apr 2023 12:20:24 +0200 Subject: [PATCH 24/26] Cleanup: const variables and functional-style cast for enums. --- .../intern/node_tree_field_inferencing.cc | 8 ++++---- .../geometry/nodes/node_geo_simulation_input.cc | 11 ++++------- .../geometry/nodes/node_geo_simulation_output.cc | 14 ++++++-------- 3 files changed, 14 insertions(+), 19 deletions(-) diff --git a/source/blender/blenkernel/intern/node_tree_field_inferencing.cc b/source/blender/blenkernel/intern/node_tree_field_inferencing.cc index 687286ae239..3e30f089f28 100644 --- a/source/blender/blenkernel/intern/node_tree_field_inferencing.cc +++ b/source/blender/blenkernel/intern/node_tree_field_inferencing.cc @@ -326,10 +326,10 @@ static bool propagate_special_data_requirements( /* Sync field state between simulation nodes and schedule another pass if necessary. */ if (node.type == GEO_NODE_SIMULATION_INPUT) { - const NodeGeometrySimulationInput &data = *static_cast( + const NodeGeometrySimulationInput &data = *static_cast( node.storage); if (const bNode *output_node = tree.node_by_id(data.output_node_id)) { - eFieldStateSyncResult sync_result = simulation_nodes_field_state_sync( + const eFieldStateSyncResult sync_result = simulation_nodes_field_state_sync( node, *output_node, field_state_by_socket_id); if ((bool)(sync_result & eFieldStateSyncResult::CHANGED_B)) { need_update = true; @@ -338,10 +338,10 @@ static bool propagate_special_data_requirements( } else if (node.type == GEO_NODE_SIMULATION_OUTPUT) { for (const bNode *input_node : tree.nodes_by_type("GeometryNodeSimulationInput")) { - const NodeGeometrySimulationInput &data = *static_cast( + const NodeGeometrySimulationInput &data = *static_cast( input_node->storage); if (node.identifier == data.output_node_id) { - eFieldStateSyncResult sync_result = simulation_nodes_field_state_sync( + const eFieldStateSyncResult sync_result = simulation_nodes_field_state_sync( *input_node, node, field_state_by_socket_id); if ((bool)(sync_result & eFieldStateSyncResult::CHANGED_A)) { need_update = true; diff --git a/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc b/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc index 07f72711c62..43be25b9463 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc @@ -38,13 +38,13 @@ static void copy_next_simulation_state(lf::Params ¶ms, /* TODO */ if (socket_type == SOCK_GEOMETRY) { - const bke::sim::GeometrySimulationStateItem &geo_state_item = static_cast(state_item); + const bke::sim::GeometrySimulationStateItem &geo_state_item = + static_cast(state_item); const GeometrySet &src = geo_state_item.geometry(); /* First output parameter is "Delta Time", state item parameters start at index 1. */ params.set_output(index + 1, src); } else { - // if (const void *src = state_item.data()) { if (const void *src = cpptype.default_value()) { /* First output parameter is "Delta Time", state item parameters start at index 1. */ void *dst = params.get_output_data_ptr(index + 1); @@ -108,7 +108,7 @@ class LazyFunctionForSimulationInputNode final : public LazyFunction { continue; } copy_initial_simulation_state( - params, i, static_cast(simulation_items_[i].socket_type)); + params, i, eNodeSocketDatatype(simulation_items_[i].socket_type)); } } else { @@ -120,10 +120,7 @@ class LazyFunctionForSimulationInputNode final : public LazyFunction { const bke::sim::SimulationStateItem *state_item = prev_zone_state->items[i].get(); if (state_item != nullptr) { copy_next_simulation_state( - params, - i, - static_cast(simulation_items_[i].socket_type), - *state_item); + params, i, eNodeSocketDatatype(simulation_items_[i].socket_type), *state_item); } } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc b/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc index df43f1f15b6..f8999bcd9c6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc @@ -32,8 +32,7 @@ bool is_simulation_item_type_supported(const eNodeSocketDatatype socket_type) static std::unique_ptr socket_declaration_for_simulation_item( const NodeSimulationItem &item, const eNodeSocketInOut in_out, const int index) { - BLI_assert( - is_simulation_item_type_supported(static_cast(item.socket_type))); + BLI_assert(is_simulation_item_type_supported(eNodeSocketDatatype(item.socket_type))); std::unique_ptr decl; switch (eNodeSocketDatatype(item.socket_type)) { @@ -108,7 +107,7 @@ static bool simulation_items_unique_name_check(void *arg, const char *name) NodeSimulationItem *simulation_item_add_from_socket(NodeGeometrySimulationOutput &storage, const bNodeSocket &socket) { - if (!is_simulation_item_type_supported(static_cast(socket.type))) { + if (!is_simulation_item_type_supported(eNodeSocketDatatype(socket.type))) { return nullptr; } @@ -148,7 +147,7 @@ const CPPType &get_simulation_item_cpp_type(const eNodeSocketDatatype socket_typ const CPPType &get_simulation_item_cpp_type(const NodeSimulationItem &item) { - return get_simulation_item_cpp_type(static_cast(item.socket_type)); + return get_simulation_item_cpp_type(eNodeSocketDatatype(item.socket_type)); } static std::unique_ptr make_simulation_state_item( @@ -265,8 +264,8 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction { continue; } - new_zone_state.items[i] = make_simulation_state_item( - static_cast(item.socket_type), input_data); + new_zone_state.items[i] = make_simulation_state_item(eNodeSocketDatatype(item.socket_type), + input_data); } if (all_available) { @@ -286,8 +285,7 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction { if (state_item == nullptr) { continue; } - copy_simulation_state_output( - params, i, static_cast(item.socket_type), *state_item); + copy_simulation_state_output(params, i, eNodeSocketDatatype(item.socket_type), *state_item); } params.set_default_remaining_outputs(); } -- 2.30.2 From cfe957c584112da335a1ea11a7f9406e326f52d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Mon, 24 Apr 2023 10:47:50 +0200 Subject: [PATCH 25/26] Cleanup: Comments and better description of the input/output copy functions. --- .../nodes/geometry/node_geometry_util.hh | 4 ++ .../nodes/node_geo_simulation_input.cc | 39 ++++--------------- .../nodes/node_geo_simulation_output.cc | 17 ++++---- 3 files changed, 22 insertions(+), 38 deletions(-) diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index 62081102a8f..9ad2e8e6237 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -142,5 +142,9 @@ const CPPType &get_simulation_item_cpp_type(const NodeSimulationItem &item); /** \warning Return value will be reallocated when items are added or removed. */ NodeSimulationItem *simulation_item_add_from_socket(NodeGeometrySimulationOutput &storage, const bNodeSocket &socket); +void copy_simulation_state_to_output_param(lf::Params ¶ms, + int index, + eNodeSocketDatatype socket_type, + const bke::sim::SimulationStateItem &state_item); } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc b/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc index 3e8b707b8d0..fee79081c15 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc @@ -14,9 +14,10 @@ namespace blender::nodes { -static void copy_initial_simulation_state(lf::Params ¶ms, - const int index, - const eNodeSocketDatatype socket_type) +/** Copy a node input parameter to the matching output parameter. */ +static void copy_input_to_output_param(lf::Params ¶ms, + const int index, + const eNodeSocketDatatype socket_type) { const CPPType &cpptype = get_simulation_item_cpp_type(socket_type); @@ -29,31 +30,6 @@ static void copy_initial_simulation_state(lf::Params ¶ms, } } -static void copy_next_simulation_state(lf::Params ¶ms, - const int index, - const eNodeSocketDatatype socket_type, - const bke::sim::SimulationStateItem &state_item) -{ - const CPPType &cpptype = get_simulation_item_cpp_type(socket_type); - - /* TODO */ - if (socket_type == SOCK_GEOMETRY) { - const bke::sim::GeometrySimulationStateItem &geo_state_item = - static_cast(state_item); - const GeometrySet &src = geo_state_item.geometry(); - /* First output parameter is "Delta Time", state item parameters start at index 1. */ - params.set_output(index + 1, src); - } - else { - if (const void *src = cpptype.default_value()) { - /* First output parameter is "Delta Time", state item parameters start at index 1. */ - void *dst = params.get_output_data_ptr(index + 1); - cpptype.copy_construct(src, dst); - params.output_set(index + 1); - } - } -} - } // namespace blender::nodes namespace blender::nodes::node_geo_simulation_input_cc { @@ -107,7 +83,7 @@ class LazyFunctionForSimulationInputNode final : public LazyFunction { if (params.output_was_set(i + 1)) { continue; } - copy_initial_simulation_state( + copy_input_to_output_param( params, i, eNodeSocketDatatype(simulation_items_[i].socket_type)); } } @@ -119,8 +95,9 @@ class LazyFunctionForSimulationInputNode final : public LazyFunction { } const bke::sim::SimulationStateItem *state_item = prev_zone_state->items[i].get(); if (state_item != nullptr) { - copy_next_simulation_state( - params, i, eNodeSocketDatatype(simulation_items_[i].socket_type), *state_item); + /* First output parameter is "Delta Time", state item parameters start at index 1. */ + copy_simulation_state_to_output_param( + params, i + 1, eNodeSocketDatatype(simulation_items_[i].socket_type), *state_item); } } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc b/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc index 25bc25b55c1..af5153c3f62 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc @@ -150,25 +150,27 @@ const CPPType &get_simulation_item_cpp_type(const NodeSimulationItem &item) return get_simulation_item_cpp_type(eNodeSocketDatatype(item.socket_type)); } +/** Create a simulation state item from parameter input data. */ static std::unique_ptr make_simulation_state_item( const eNodeSocketDatatype socket_type, void *input_data) { - /* TODO */ if (socket_type == SOCK_GEOMETRY) { GeometrySet *input_geometry = static_cast(input_data); input_geometry->ensure_owns_direct_data(); return std::make_unique(*input_geometry); } else { + /* TODO: Implement for non-geometry state items. */ GeometrySet geometry; return std::make_unique(geometry); } } -static void copy_simulation_state_output(lf::Params ¶ms, - const int index, - const eNodeSocketDatatype socket_type, - const bke::sim::SimulationStateItem &state_item) +/** Copy the current simulation state to the node output parameter. */ +void copy_simulation_state_to_output_param(lf::Params ¶ms, + const int index, + const eNodeSocketDatatype socket_type, + const bke::sim::SimulationStateItem &state_item) { const CPPType &cpptype = get_simulation_item_cpp_type(socket_type); @@ -178,7 +180,7 @@ static void copy_simulation_state_output(lf::Params ¶ms, params.set_output(index, geo_state_item.geometry()); } else { - /* TODO */ + /* TODO: Implement for non-geometry state items. */ if (const void *src = cpptype.default_value()) { void *dst = params.get_output_data_ptr(index); cpptype.copy_construct(src, dst); @@ -299,7 +301,8 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction { if (state_item == nullptr) { continue; } - copy_simulation_state_output(params, i, eNodeSocketDatatype(item.socket_type), *state_item); + copy_simulation_state_to_output_param( + params, i, eNodeSocketDatatype(item.socket_type), *state_item); } params.set_default_remaining_outputs(); } -- 2.30.2 From f8ffd4f5b85c1f2f8ee93fc3aea0dabeb098cc48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Mon, 24 Apr 2023 11:04:44 +0200 Subject: [PATCH 26/26] Functional cast for enum class result checks. --- .../blender/blenkernel/intern/node_tree_field_inferencing.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/node_tree_field_inferencing.cc b/source/blender/blenkernel/intern/node_tree_field_inferencing.cc index 3e30f089f28..96b3123c1e1 100644 --- a/source/blender/blenkernel/intern/node_tree_field_inferencing.cc +++ b/source/blender/blenkernel/intern/node_tree_field_inferencing.cc @@ -331,7 +331,7 @@ static bool propagate_special_data_requirements( if (const bNode *output_node = tree.node_by_id(data.output_node_id)) { const eFieldStateSyncResult sync_result = simulation_nodes_field_state_sync( node, *output_node, field_state_by_socket_id); - if ((bool)(sync_result & eFieldStateSyncResult::CHANGED_B)) { + if (bool(sync_result & eFieldStateSyncResult::CHANGED_B)) { need_update = true; } } @@ -343,7 +343,7 @@ static bool propagate_special_data_requirements( if (node.identifier == data.output_node_id) { const eFieldStateSyncResult sync_result = simulation_nodes_field_state_sync( *input_node, node, field_state_by_socket_id); - if ((bool)(sync_result & eFieldStateSyncResult::CHANGED_A)) { + if (bool(sync_result & eFieldStateSyncResult::CHANGED_A)) { need_update = true; } } -- 2.30.2