diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 28aa407f633..001aef4b6cc 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -345,11 +345,6 @@ typedef struct bNodeType { /* Execute a geometry node. */ NodeGeometryExecFunction geometry_node_execute; - /** - * If true, the geometry nodes evaluator can call the execute function multiple times to improve - * performance by specifying required data in one call and using it for calculations in another. - */ - bool geometry_node_execute_supports_laziness; /* Declares which sockets the node has. */ NodeDeclareFunction declare; diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh index 208d51f13d3..baacd1bedbe 100644 --- a/source/blender/nodes/NOD_geometry_exec.hh +++ b/source/blender/nodes/NOD_geometry_exec.hh @@ -194,8 +194,6 @@ class GeoNodeExecParams { /** * Returns true when the output has to be computed. - * Nodes that support laziness could use the #lazy_output_is_required variant to possibly avoid - * some computations. */ bool output_is_required(StringRef identifier) const { @@ -203,29 +201,6 @@ class GeoNodeExecParams { return params_.get_output_usage(index) != lf::ValueUsage::Unused; } - /** - * Tell the evaluator that a specific input is required. - * This returns true when the input will only be available in the next execution. - * False is returned if the input is available already. - * This can only be used when the node supports laziness. - */ - bool lazy_require_input(StringRef identifier) - { - const int index = this->get_input_index(identifier); - return params_.try_get_input_data_ptr_or_request(index) == nullptr; - } - - /** - * Asks the evaluator if a specific output is required right now. If this returns false, the - * value might still need to be computed later. - * This can only be used when the node supports laziness. - */ - bool lazy_output_is_required(StringRef identifier) - { - const int index = this->get_output_index(identifier); - return params_.get_output_usage(index) == lf::ValueUsage::Used; - } - /** * Get the node that is currently being executed. */ diff --git a/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh b/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh index 793798c4974..7401fbfca8c 100644 --- a/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh +++ b/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh @@ -221,6 +221,8 @@ class GeometryNodesLazyFunctionLogger : public fn::lazy_function::GraphExecutor: const lf::Context &context) const override; }; +std::unique_ptr get_switch_node_lazy_function(const bNode &node); + /** * Tells the lazy-function graph evaluator which nodes have side effects based on the current * context. For example, the same viewer node can have side effects in one context, but not in diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc index 6873c557310..d4d7ecf6ae3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_switch.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc @@ -5,14 +5,9 @@ #include "UI_interface.h" #include "UI_resources.h" -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" - -#include "BKE_material.h" - #include "NOD_socket_search_link.hh" -#include "FN_multi_function_signature.hh" +#include "FN_field_cpp_type.hh" namespace blender::nodes::node_geo_switch_cc { @@ -149,149 +144,128 @@ static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) } } -template void switch_fields(GeoNodeExecParams ¶ms, const StringRef suffix) -{ - if (params.lazy_require_input("Switch")) { - return; +class LazyFunctionForSwitchNode : public LazyFunction { + public: + LazyFunctionForSwitchNode(const bNode &node) + { + const NodeSwitch &storage = node_storage(node); + const eNodeSocketDatatype data_type = eNodeSocketDatatype(storage.input_type); + const bNodeSocketType *socket_type = nullptr; + for (const bNodeSocket *socket : node.output_sockets()) { + if (socket->type == data_type) { + socket_type = socket->typeinfo; + break; + } + } + BLI_assert(socket_type != nullptr); + const CPPType &cpp_type = *socket_type->geometry_nodes_cpp_type; + + inputs_.append_as("Condition", CPPType::get>()); + inputs_.append_as("False", cpp_type, lf::ValueUsage::Maybe); + inputs_.append_as("True", cpp_type, lf::ValueUsage::Maybe); + outputs_.append_as("Value", cpp_type); } - const std::string name_false = "False" + suffix; - const std::string name_true = "True" + suffix; - const std::string name_output = "Output" + suffix; + void execute_impl(lf::Params ¶ms, const lf::Context & /*context*/) const override + { + const ValueOrField condition = params.get_input>(0); + if (condition.is_field()) { + Field condition_field = condition.as_field(); + if (condition_field.node().depends_on_input()) { + this->execute_field(condition.as_field(), params); + return; + } + const bool condition_bool = fn::evaluate_constant_field(condition_field); + this->execute_single(condition_bool, params); + return; + } + this->execute_single(condition.as_value(), params); + } - Field switches_field = params.get_input>("Switch"); - if (switches_field.node().depends_on_input()) { - /* The switch has to be incorporated into the field. Both inputs have to be evaluated. */ - const bool require_false = params.lazy_require_input(name_false); - const bool require_true = params.lazy_require_input(name_true); - if (require_false | require_true) { + static constexpr int false_input_index = 1; + static constexpr int true_input_index = 2; + + void execute_single(const bool condition, lf::Params ¶ms) const + { + const int input_to_forward = condition ? true_input_index : false_input_index; + const int input_to_ignore = condition ? false_input_index : true_input_index; + + params.set_input_unused(input_to_ignore); + void *value_to_forward = params.try_get_input_data_ptr_or_request(input_to_forward); + if (value_to_forward == nullptr) { + /* Try again when the value is available. */ return; } - Field falses_field = params.extract_input>(name_false); - Field trues_field = params.extract_input>(name_true); + const CPPType &type = *outputs_[0].type; + void *output_ptr = params.get_output_data_ptr(0); + type.move_construct(value_to_forward, output_ptr); + params.output_set(0); + } - static auto switch_fn = mf::build::SI3_SO( - "Switch", [](bool condition, const T &false_value, const T &true_value) { - return condition ? true_value : false_value; + void execute_field(Field condition, lf::Params ¶ms) const + { + /* When the condition is a non-constant field, we need both inputs. */ + void *false_value_or_field = params.try_get_input_data_ptr_or_request(false_input_index); + void *true_value_or_field = params.try_get_input_data_ptr_or_request(true_input_index); + if (ELEM(nullptr, false_value_or_field, true_value_or_field)) { + /* Try again when inputs are available. */ + return; + } + + const CPPType &type = *outputs_[0].type; + const fn::ValueOrFieldCPPType &value_or_field_type = *fn::ValueOrFieldCPPType::get_from_self( + type); + const CPPType &value_type = value_or_field_type.value; + const MultiFunction &switch_multi_function = this->get_switch_multi_function(value_type); + + GField false_field = value_or_field_type.as_field(false_value_or_field); + GField true_field = value_or_field_type.as_field(true_value_or_field); + + GField output_field{FieldOperation::Create( + switch_multi_function, + {std::move(condition), std::move(false_field), std::move(true_field)})}; + + void *output_ptr = params.get_output_data_ptr(0); + value_or_field_type.construct_from_field(output_ptr, std::move(output_field)); + params.output_set(0); + } + + const MultiFunction &get_switch_multi_function(const CPPType &type) const + { + const MultiFunction *switch_multi_function = nullptr; + type.to_static_type_tag( + [&](auto type_tag) { + using T = typename decltype(type_tag)::type; + if constexpr (std::is_void_v) { + BLI_assert_unreachable(); + } + else { + static auto switch_fn = mf::build::SI3_SO( + "Switch", [](const bool condition, const T &false_value, const T &true_value) { + return condition ? true_value : false_value; + }); + switch_multi_function = &switch_fn; + } }); - - auto switch_op = std::make_shared(FieldOperation( - std::move(switch_fn), - {std::move(switches_field), std::move(falses_field), std::move(trues_field)})); - - params.set_output(name_output, Field(switch_op, 0)); + BLI_assert(switch_multi_function != nullptr); + return *switch_multi_function; } - else { - /* The switch input is constant, so just evaluate and forward one of the inputs. */ - const bool switch_value = fn::evaluate_constant_field(switches_field); - if (switch_value) { - params.set_input_unused(name_false); - if (params.lazy_require_input(name_true)) { - return; - } - params.set_output(name_output, params.extract_input>(name_true)); - } - else { - params.set_input_unused(name_true); - if (params.lazy_require_input(name_false)) { - return; - } - params.set_output(name_output, params.extract_input>(name_false)); - } - } -} - -template void switch_no_fields(GeoNodeExecParams ¶ms, const StringRef suffix) -{ - if (params.lazy_require_input("Switch_001")) { - return; - } - bool switch_value = params.get_input("Switch_001"); - - const std::string name_false = "False" + suffix; - const std::string name_true = "True" + suffix; - const std::string name_output = "Output" + suffix; - - if (switch_value) { - params.set_input_unused(name_false); - if (params.lazy_require_input(name_true)) { - return; - } - params.set_output(name_output, params.extract_input(name_true)); - } - else { - params.set_input_unused(name_true); - if (params.lazy_require_input(name_false)) { - return; - } - params.set_output(name_output, params.extract_input(name_false)); - } -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - const NodeSwitch &storage = node_storage(params.node()); - const eNodeSocketDatatype data_type = eNodeSocketDatatype(storage.input_type); - - switch (data_type) { - - case SOCK_FLOAT: { - switch_fields(params, ""); - break; - } - case SOCK_INT: { - switch_fields(params, "_001"); - break; - } - case SOCK_BOOLEAN: { - switch_fields(params, "_002"); - break; - } - case SOCK_VECTOR: { - switch_fields(params, "_003"); - break; - } - case SOCK_RGBA: { - switch_fields(params, "_004"); - break; - } - case SOCK_STRING: { - switch_fields(params, "_005"); - break; - } - case SOCK_GEOMETRY: { - switch_no_fields(params, "_006"); - break; - } - case SOCK_OBJECT: { - switch_no_fields(params, "_007"); - break; - } - case SOCK_COLLECTION: { - switch_no_fields(params, "_008"); - break; - } - case SOCK_TEXTURE: { - switch_no_fields(params, "_009"); - break; - } - case SOCK_MATERIAL: { - switch_no_fields(params, "_010"); - break; - } - case SOCK_IMAGE: { - switch_no_fields(params, "_011"); - break; - } - default: - BLI_assert_unreachable(); - break; - } -} +}; } // namespace blender::nodes::node_geo_switch_cc +namespace blender::nodes { + +std::unique_ptr get_switch_node_lazy_function(const bNode &node) +{ + using namespace node_geo_switch_cc; + BLI_assert(node.type == GEO_NODE_SWITCH); + return std::make_unique(node); +} + +} // namespace blender::nodes + void register_node_type_geo_switch() { namespace file_ns = blender::nodes::node_geo_switch_cc; @@ -303,8 +277,6 @@ void register_node_type_geo_switch() ntype.initfunc = file_ns::node_init; ntype.updatefunc = file_ns::node_update; node_type_storage(&ntype, "NodeSwitch", node_free_standard_storage, node_copy_standard_storage); - ntype.geometry_node_execute = file_ns::node_geo_exec; - ntype.geometry_node_execute_supports_laziness = true; ntype.gather_link_search_ops = file_ns::node_gather_link_searches; ntype.draw_buttons = file_ns::node_layout; nodeRegisterType(&ntype); diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index 80af8ea32ba..ad272993ea1 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -73,10 +73,7 @@ static void lazy_function_interface_from_node(const bNode &node, Vector &r_outputs) { const bool is_muted = node.is_muted(); - const bool supports_laziness = node.typeinfo->geometry_node_execute_supports_laziness || - node.is_group(); - const lf::ValueUsage input_usage = supports_laziness ? lf::ValueUsage::Maybe : - lf::ValueUsage::Used; + const lf::ValueUsage input_usage = lf::ValueUsage::Used; for (const bNodeSocket *socket : node.input_sockets()) { if (!socket->is_available()) { continue; @@ -1331,6 +1328,10 @@ struct GeometryNodesLazyFunctionGraphBuilder { this->handle_viewer_node(*bnode); break; } + case GEO_NODE_SWITCH: { + this->handle_switch_node(*bnode); + break; + } default: { if (node_type->geometry_node_execute) { this->handle_geometry_node(*bnode); @@ -1572,6 +1573,31 @@ struct GeometryNodesLazyFunctionGraphBuilder { mapping_->viewer_node_map.add(&bnode, &lf_node); } + void handle_switch_node(const bNode &bnode) + { + std::unique_ptr lazy_function = get_switch_node_lazy_function(bnode); + lf::FunctionNode &lf_node = lf_graph_->add_function(*lazy_function); + lf_graph_info_->functions.append(std::move(lazy_function)); + + int input_index = 0; + for (const bNodeSocket *bsocket : bnode.input_sockets()) { + if (bsocket->is_available()) { + lf::InputSocket &lf_socket = lf_node.input(input_index); + input_socket_map_.add(bsocket, &lf_socket); + mapping_->bsockets_by_lf_socket_map.add(&lf_socket, bsocket); + input_index++; + } + } + for (const bNodeSocket *bsocket : bnode.output_sockets()) { + if (bsocket->is_available()) { + lf::OutputSocket &lf_socket = lf_node.output(0); + output_socket_map_.add(bsocket, &lf_socket); + mapping_->bsockets_by_lf_socket_map.add(&lf_socket, bsocket); + break; + } + } + } + void handle_undefined_node(const bNode &bnode) { Vector used_outputs;